鸿蒙智能体开发实战:32.鸿蒙壁纸大师 - API认证与会话管理

前言

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 接口,确认返回的 agentSessionIdagentSessionTtl
  • agent_routes.py 的入口打印 agent-session-id 和请求体,快速定位问题。
  • 使用 Redis 客户端查看会话存储状态(生产环境)。

调试时请勿将真实的 VOLCANIC_API_KEYWEBHOOK_API_SECRET 提交到代码仓库。

总结

本文深入介绍了鸿蒙壁纸大师的 API 认证与会话管理 实现:

  1. 认证流程:平台级认证 + 用户级授权的双层设计
  2. 会话管理:initialize → notifications/initialized 的完整流程
  3. 会话重建:服务重启或过期时的自动重建机制
  4. 用户授权:authorize/deauthorize 的实现与身份标识
  5. 设备信息提取:从系统变量中获取设备型号和推送 ID
  6. 任务管理:任务状态跟踪、取消、上下文清理
  7. 安全实践:会话加固、黑名单、生产环境建议

健壮的会话管理是 A2A 服务稳定运行的基础。下一篇文章将介绍本项目的核心------多轮交互工作流设计


如果这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐ 和关注 🎯!你的支持是我持续创作的动力!

相关资源

相关推荐
花开彼岸天~1 小时前
鸿蒙实战:导航栏与顶部栏设计规范 HmTitleBar
华为·harmonyos·鸿蒙系统·设计规范
星释1 小时前
鸿蒙智能体开发实战:33.鸿蒙壁纸大师 - 多轮交互工作流设计
华为·交互·harmonyos·鸿蒙
Gary Studio2 小时前
嵌入式大模型边缘部署(MTK方向)
ai·大模型·mtk·边缘部署
●VON2 小时前
HarmonyKit | 鸿蒙新特性:router 导航 API 从 pushUrl 到 UIContext 的演进
算法·华为·交互·harmonyos
星释2 小时前
鸿蒙智能体开发实战:31.鸿蒙壁纸大师 - 环境搭建与基础配置
算法·华为·ai·harmonyos·鸿蒙
特立独行的猫a2 小时前
鸿蒙PC开源软件迁移与多语言三方库移植实战课程讲稿(三、命令行工具移植实战——以OpenSSH为主线)
华为·harmonyos·三方库移植·鸿蒙pc
土星云SaturnCloud2 小时前
边缘计算在储气库调峰智能管控中的应用:架构设计与技术解析
服务器·人工智能·ai·边缘计算
程序员黑豆2 小时前
鸿蒙应用开发之全局状态管理:AppStorageV2轻松实现登录到个人中心数据共享
华为·harmonyos·鸿蒙
曦尧3 小时前
Bun:面向 JavaScript/TypeScript 的全能超高速一体化工具链
ai·自动化