前言
A2A 协议的核心通信是通过 JSON-RPC 实现的。在智能体执行引擎调用服务端 API 之前,需要完成认证 和会话建立两个关键步骤。本文将深入讲解这两个环节的实现细节。
会话管理是 A2A 服务的基石。一个健壮的会话管理机制,能有效防止未授权访问、处理会话过期、并在服务重启后优雅恢复。
一、A2A 认证流程
1.1 完整认证流程
鸿蒙 A2A 的认证流程分为两步:
plaintext
第一步:平台级认证(小艺开放平台 → 服务端)
小艺开放平台配置 Access Key 和 Secret Key
每次 A2A 请求携带 agent-session-id 头
服务端验证请求合法性
第二步:用户级授权(用户 → 服务端)
用户通过小艺授权 → authorize 方法
服务端返回 agentLoginSessionId
后续请求可携带用户身份标识

上图展示了从会话初始化到用户授权的完整 A2A 认证流程:initialize 建立会话 → notifications/initialized 激活 → authorize 用户授权
1.2 平台配置
在小艺开放平台上配置认证信息:
json
{
"authentication": {
"type": "access_key",
"accessKey": "your_access_key",
"secretKey": "your_secret_key",
"tokenEndpoint": "https://your-server.com/auth/token"
}
}
1.3 服务端验证
python
# routes/agent_routes.py - 请求处理入口
from fastapi import APIRouter, Header, Request
from typing import Optional
router = APIRouter(prefix="/agent", tags=["agent"])
@router.post("/message")
async def handle_agent_message(
request: Request,
agent_session_id: Optional[str] = Header(None, alias="agent-session-id"),
x_request_id: Optional[str] = Header(None, alias="X-Request-ID"),
):
"""
统一的 Agent 消息处理接口
所有 A2A 协议的 RPC 方法都通过此端点进入
Header 说明:
- agent-session-id: 会话 ID(由 initialize 方法返回)
- X-Request-ID: 请求追踪 ID(可选)
"""
# 从 app.state 获取共享状态
agent_sessions = request.app.state.agent_sessions
conversation_contexts = request.app.state.conversation_contexts
task_states = request.app.state.task_states
request_id = x_request_id or str(uuid.uuid4())
# 解析 JSON-RPC 请求
body = await request.json()
jsonrpc_request = JsonRpcRequest(**body)
method = jsonrpc_request.method
# 路由分发
if method == "initialize":
result = await handle_initialize(jsonrpc_request, request_id, agent_sessions)
elif method == "message/stream":
return await handle_message_stream(...)
elif method == "authorize":
result = await handle_authorize(...)
# ... 其他方法
二、Session 管理实现
2.1 会话初始化
python
# routes/agent_routes.py - Session 管理
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any
async def handle_initialize(
request: JsonRpcRequest,
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]]
) -> JSONResponse:
"""
处理 initialize 方法
功能:创建新的会话,返回 session_id
- 每次调用生成唯一的 agentSessionId
- 会话有效期 7 天(604800 秒)
- 存储会话创建时间和状态
"""
session_id = uuid.uuid4().hex
session_ttl = 7 * 24 * 60 * 60 # 7 天
# 存储会话信息
agent_sessions[session_id] = {
"created_at": datetime.utcnow(),
"expires_at": datetime.utcnow() + timedelta(seconds=session_ttl),
"status": "initialized",
}
# 返回 JSON-RPC 响应
response = JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={
"version": "1.0",
"agentSessionId": session_id,
"agentSessionTtl": session_ttl,
}
)
return JSONResponse(content=response.model_dump())
2.2 初始化通知
python
async def handle_initialized(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]]
) -> JSONResponse:
"""
处理 notifications/initialized 方法
客户端告知服务端初始化已完成
服务端将会话状态从 "initialized" 更新为 "active"
"""
if not agent_session_id or agent_session_id not in agent_sessions:
return JSONResponse(
status_code=401,
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
error=JsonRpcError(code=401, message="Invalid session ID")
).model_dump()
)
# 更新会话状态为活跃
agent_sessions[agent_session_id]["status"] = "active"
return JSONResponse(content={})
2.3 会话过期自动重建
python
# 在 handle_message_stream 中处理会话过期
async def handle_message_stream(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]],
conversation_contexts: Dict[str, List[Dict[str, Any]]],
task_states: Dict[str, Dict[str, Any]],
) -> StreamingResponse:
"""
处理 message/stream 方法
关键特性:自动重建过期会话
当服务重启或会话过期时,自动创建新会话
"""
# 如果 session_id 无效,自动创建新 session
if not agent_session_id or agent_session_id not in agent_sessions:
logger.warning(f"无效的 session_id,自动创建新 session")
agent_session_id = uuid.uuid4().hex
agent_sessions[agent_session_id] = {
"created_at": datetime.utcnow(),
"expires_at": datetime.utcnow() + timedelta(seconds=7 * 24 * 60 * 60),
"status": "active",
}
logger.info(f"创建新 session: {agent_session_id[:16]}...")
# ... 后续处理
2.4 会话过期检测
python
class SessionManager:
"""
会话管理器
提供会话的生命周期管理和过期清理
"""
def __init__(self):
self.sessions: Dict[str, Dict] = {}
def create_session(self, ttl_seconds: int = 604800) -> dict:
"""创建新会话"""
session_id = uuid.uuid4().hex
now = datetime.utcnow()
self.sessions[session_id] = {
"session_id": session_id,
"created_at": now,
"expires_at": now + timedelta(seconds=ttl_seconds),
"last_active": now,
"status": "active",
"request_count": 0,
}
return {
"agentSessionId": session_id,
"agentSessionTtl": ttl_seconds,
"version": "1.0",
}
def validate_session(self, session_id: str) -> bool:
"""验证会话是否有效"""
session = self.sessions.get(session_id)
if not session:
return False
# 检查是否过期
if datetime.utcnow() > session["expires_at"]:
del self.sessions[session_id]
return False
# 更新最后活跃时间
session["last_active"] = datetime.utcnow()
session["request_count"] += 1
return True
def refresh_session(self, session_id: str, ttl_seconds: int = 604800) -> bool:
"""刷新会话有效期"""
session = self.sessions.get(session_id)
if not session:
return False
session["expires_at"] = datetime.utcnow() + timedelta(seconds=ttl_seconds)
return True
def cleanup_expired(self) -> int:
"""清理过期会话"""
now = datetime.utcnow()
expired = [
sid for sid, sess in self.sessions.items()
if now > sess["expires_at"]
]
for sid in expired:
del self.sessions[sid]
return len(expired)
# 使用示例
session_manager = SessionManager()
# 创建会话
session = session_manager.create_session()
print(f"Session ID: {session['agentSessionId']}")
# 验证会话
if session_manager.validate_session(session['agentSessionId']):
print("会话有效")
else:
print("会话已过期")
三、用户授权(Authorize/Deauthorize)
3.1 授权流程
鸿蒙 A2A 支持用户级授权,允许智能体获取用户身份信息:
python
async def handle_authorize(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]]
) -> JSONResponse:
"""
处理 authorize 方法
用户授权流程:
1. 小艺平台请求用户授权
2. 用户同意后,平台调用 authorize 方法
3. 服务端生成 agentLoginSessionId 返回
4. 后续请求可携带该 ID 标识用户身份
"""
params = request.params or {}
message = params.get("message", {})
# 从消息中提取 authCode
auth_code = None
for part in message.get("parts", []):
if part.get("kind") == "data" and part.get("data"):
auth_code = part.get("data", {}).get("authCode")
break
if not auth_code:
return JSONResponse(
status_code=400,
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
error=JsonRpcError(code=400, message="Missing authCode")
).model_dump()
)
# 生成用户授权会话 ID
agent_login_session_id = uuid.uuid4().hex
agent_sessions[agent_session_id]["agentLoginSessionId"] = agent_login_session_id
agent_sessions[agent_session_id]["authCode"] = auth_code
return JSONResponse(
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={
"version": "1.0",
"agentLoginSessionId": agent_login_session_id
}
).model_dump()
)
3.2 解授权
python
async def handle_deauthorize(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]]
) -> JSONResponse:
"""
处理 deauthorize 方法
用户撤销授权时调用
清除会话中的用户身份信息
"""
params = request.params or {}
message = params.get("message", {})
agent_login_session_id = None
for part in message.get("parts", []):
if part.get("kind") == "data" and part.get("data"):
agent_login_session_id = part.get("data", {}).get("agentLoginSessionId")
break
if agent_login_session_id:
# 清除授权信息
agent_sessions[agent_session_id].pop("agentLoginSessionId", None)
agent_sessions[agent_session_id].pop("authCode", None)
return JSONResponse(
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={"version": "1.0"}
).model_dump()
)
四、设备系统变量提取
4.1 从请求中提取设备信息
鸿蒙智能体执行引擎会在请求中携带设备信息(系统变量),服务端可以从中提取设备类型、型号等信息,用于后续优化:
python
# services/wallpaper_service.py - 设备信息提取
from typing import List, Dict, Any
def extract_device_info(message_parts: List[Dict]) -> Dict[str, Any]:
"""
从消息 parts 中提取设备信息
A2A 消息中的设备系统变量格式:
{
"kind": "data",
"data": {
"variables": {
"systemVariables": {
"device_type": "HUAWEI MATE 60 PRO",
"display_version": "HarmonyOS 5.0",
"market_name": "HUAWEI Mate 60 Pro",
"push_id": "xxx"
}
}
}
}
"""
device_info = {
"device_type": None,
"display_version": None,
"market_name": None,
"push_id": None
}
for part in message_parts:
if part.get("kind") == "data" and part.get("data"):
variables = part.get("data", {}).get("variables", {})
system_variables = variables.get("systemVariables", {})
if system_variables.get("device_type"):
device_info["device_type"] = system_variables.get("device_type")
if system_variables.get("display_version"):
device_info["display_version"] = system_variables.get("display_version")
if system_variables.get("market_name"):
device_info["market_name"] = system_variables.get("market_name")
if system_variables.get("push_id"):
device_info["push_id"] = system_variables.get("push_id")
return device_info
4.2 在流程中应用设备信息
python
# 在 message/stream 处理中提取和使用设备信息
async def handle_message_stream(...):
"""处理消息流"""
# 提取用户消息
user_text = get_text_from_parts(message.get("parts", []))
# 提取设备信息(关键步骤)
device_info = extract_device_info(message.get("parts", []))
device_type = device_info.get("device_type")
push_id = device_info.get("push_id")
if device_type:
logger.info(f"设备类型:{device_type}")
if push_id:
logger.info(f"push_id: {push_id}")
# 后续使用设备信息优化壁纸尺寸
# 详情见第 38 篇
五、任务状态管理
5.1 任务状态跟踪
python
# 任务状态管理
# 在 main.py 中定义
task_states: Dict[str, Dict[str, Any]] = {}
# 在 message/stream 中存储任务状态
task_states[task_id] = {
"sessionId": session_id,
"agentSessionId": agent_session_id,
"agentLoginSessionId": agent_login_session_id,
"status": "working",
"created_at": datetime.utcnow(),
}
# 标记 session 为忙碌状态
task_states[f"{session_id}_busy"] = True
# 释放忙碌状态(在 finally 块中)
# task_states[f"{session_id}_busy"] = False
5.2 任务取消
python
async def handle_tasks_cancel(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]],
task_states: Dict[str, Dict[str, Any]]
) -> JSONResponse:
"""
处理 tasks/cancel 方法
当用户取消任务时调用
"""
params = request.params or {}
task_id = params.get("id", request.id)
if task_id in task_states:
task_states[task_id]["status"] = "canceled"
logger.info(f"任务已取消:task_id={task_id}")
return JSONResponse(
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={
"id": task_id,
"status": {"state": "canceled"}
}
).model_dump()
)
5.3 上下文清理
python
async def handle_clear_context(
request: JsonRpcRequest,
agent_session_id: Optional[str],
request_id: str,
agent_sessions: Dict[str, Dict[str, Any]],
conversation_contexts: Dict[str, List[Dict[str, Any]]]
) -> JSONResponse:
"""
处理 clearContext 方法
用户要求清空对话历史时调用
创建新的上下文 ID 返回
"""
params = request.params or {}
session_id = params.get("sessionId", request.sessionId)
if session_id and session_id in conversation_contexts:
# 清空并创建新上下文
conversation_contexts[session_id] = []
new_session_id = uuid.uuid4().hex
conversation_contexts[new_session_id] = []
del conversation_contexts[session_id]
return JSONResponse(
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={
"status": {"state": "cleared"},
"newSessionId": new_session_id
}
).model_dump()
)
return JSONResponse(
content=JsonRpcResponse(
jsonrpc="2.0",
id=request.id,
result={"status": {"state": "unknown"}}
).model_dump()
)
六、安全最佳实践
6.1 Session 安全性
python
class SecureSessionManager:
"""
安全会话管理器
增强的会话安全措施
"""
def __init__(self):
self.sessions = {}
self.blacklisted_tokens = set()
def create_secure_session(self) -> dict:
"""创建安全会话"""
import secrets
# 使用 secrets 模块生成加密安全的随机数
session_id = secrets.token_hex(32)
self.sessions[session_id] = {
"created_at": datetime.utcnow(),
"expires_at": datetime.utcnow() + timedelta(days=7),
"ip_address": None,
"user_agent": None,
}
return {"agentSessionId": session_id, "agentSessionTtl": 604800}
def validate_secure_session(
self,
session_id: str,
ip_address: str = None
) -> bool:
"""安全验证"""
# 检查黑名单
if session_id in self.blacklisted_tokens:
return False
# 检查是否存在
session = self.sessions.get(session_id)
if not session:
return False
# 检查是否过期
if datetime.utcnow() > session["expires_at"]:
del self.sessions[session_id]
return False
return True
def revoke_session(self, session_id: str):
"""撤销会话"""
if session_id in self.sessions:
self.blacklisted_tokens.add(session_id)
del self.sessions[session_id]
6.2 生产环境建议
| 方案 | 开发环境 | 生产环境 |
|---|---|---|
| Session 存储 | 内存字典 | Redis / Memcached |
| 任务状态 | 内存字典 | Redis |
| 对话上下文 | 内存字典 | Redis / 数据库 |
| 速率限制 | 无 | Redis 滑动窗口 |
| 日志 | 文件 | 集中式日志系统 (ELK) |
| 会话清理 | 无 | 定时任务 |
七、调试技巧
在开发 A2A 认证与会话管理时,可以使用以下调试技巧:
- 使用
curl测试initialize接口,确认返回的agentSessionId和agentSessionTtl。 - 在
agent_routes.py的入口打印agent-session-id和请求体,快速定位问题。 - 使用 Redis 客户端查看会话存储状态(生产环境)。
调试时请勿将真实的
VOLCANIC_API_KEY或WEBHOOK_API_SECRET提交到代码仓库。
总结
本文深入介绍了鸿蒙壁纸大师的 API 认证与会话管理 实现:
- 认证流程:平台级认证 + 用户级授权的双层设计
- 会话管理:initialize → notifications/initialized 的完整流程
- 会话重建:服务重启或过期时的自动重建机制
- 用户授权:authorize/deauthorize 的实现与身份标识
- 设备信息提取:从系统变量中获取设备型号和推送 ID
- 任务管理:任务状态跟踪、取消、上下文清理
- 安全实践:会话加固、黑名单、生产环境建议
健壮的会话管理是 A2A 服务稳定运行的基础。下一篇文章将介绍本项目的核心------多轮交互工作流设计。
如果这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐ 和关注 🎯!你的支持是我持续创作的动力!