Redis如何支撑AI Agent状态管理与MCP协议落地
发布时间:2026/10/2 12:49:19来源:尧图网络
1. 项目概述Redis 并未“接入 AI”但正在成为 AI 工程化落地的关键基础设施“Redis 已正式接入 AI”——看到这个标题我第一反应是点开链接前先倒杯水因为过去三年里我亲手参与过 7 个 AI 产品从 PoC 到千万级 QPS 的全链路架构演进也反复拆解过 Redis 在其中的真实角色。它从来不是“被 AI 接入”的配角而是 AI 系统在真实生产环境中能跑得稳、扩得开、响应快的底层压舱石。标题里的“接入”二字极具误导性容易让人误以为 Redis 新增了某种内置 AI 模块或大模型推理能力——事实恰恰相反Redis 本身没有、也不需要具备任何 AI 能力它的价值恰恰在于以极简、极稳、极快的方式承载 AI 应用最脆弱、最频繁、最不可妥协的那部分状态与数据。核心关键词Redis、AI、MCP、agent-skills、Python组合在一起指向一个非常具体的工程现实当前大量基于 Agent 架构的 AI 应用尤其是本地部署、私有化交付或高实时性要求的场景正深度依赖 Redis 作为其状态中枢、技能调度总线和上下文缓存层。所谓“接入”本质是 Python 编写的 Agent 框架如 LangChain、LlamaIndex 的自定义 Agent Runner通过标准 Redis 协议RESP与 Redis 实例建立连接将原本散落在内存、文件甚至 HTTP 请求头里的临时状态统一沉淀到 Redis 的多种数据结构中。而 MCPModel Control Protocol协议的兴起则进一步放大了这一需求——MCP 本身不规定存储但它定义的 skill 调用、session 管理、tool execution trace 等关键交互天然需要一个低延迟、高并发、支持 Pub/Sub 和事务的中间件来协调Redis 是目前唯一能在单机百毫秒、集群万级 QPS 下稳定支撑这些语义的通用方案。适合谁读如果你正在用 Python 写一个带记忆、能调工具、会多步推理的 AI Agent却还在用dict存 session 或靠sqlite记日志那你就是本文最该读的人如果你负责 AI 产品的后端架构正为 Agent 响应延迟波动、技能调用丢失、上下文错乱而焦头烂额那本文给出的 Redis 数据建模方案和连接治理策略能直接帮你省下至少 20 小时的线上问题排查时间如果你是刚学完 Python 基础、正跃跃欲试想搭个“AI 助手”的新手本文会告诉你不必急着啃 Transformer先把 Redis 的hash和stream玩明白你的第一个可稳定运行的 Agent 就离你不远了。这不是讲 Redis 多酷炫而是讲——当 AI 从 demo 走向真实用户Redis 是那个默默扛住所有“意外”的人。2. 核心设计思路为什么是 Redis而不是其他数据库或消息队列2.1 不是“选 Redis”而是“别无选择”AI Agent 对状态层的硬性约束AI Agent 的运行逻辑本质上是一系列异步、状态驱动、强时效性的事件流。一个典型请求的生命周期可能包含用户输入 → LLM 生成思考链 → 解析出需调用的 skill如查天气、搜文档→ 并发执行多个 skill → 汇总结果 → LLM 生成最终回复。这个过程里有三类数据必须被可靠、低延迟地管理Session 状态用户 ID、对话历史、当前思维节点、已执行 skill 列表。它必须支持快速读写、原子更新避免多 skill 并发修改冲突、过期自动清理防止内存泄漏。Skill 执行上下文每个 skill 调用时的参数、超时设置、重试次数、返回结果。它需要支持按 session 分组、按 skill 类型索引、支持失败重试的断点续传。事件广播与协调当一个 skill 完成需通知主控 Agent 继续下一步当用户中断对话需广播取消所有待执行 skill。这要求发布/订阅机制且消息必须严格有序、不丢失。我们逐一对比主流选项方案Session 状态Skill 上下文事件协调生产就绪度关键缺陷内存 dict✅ 快✅ 快❌ 无法跨进程❌ 进程重启即丢仅限单机 demo一上生产必崩SQLite⚠️ 行锁争抢严重⚠️ 复杂查询慢❌ 无原生 Pub/Sub⚠️ 需自行实现锁和通知QPS 50 即明显延迟不支持水平扩展PostgreSQL✅ ACID 强✅ 关系建模好⚠️ LISTEN/NOTIFY 有延迟和可靠性问题✅ 成熟写入延迟 5~50ms对 sub-millisecond 级别的 skill 协调太重Kafka❌ 无状态存储❌ 无随机读✅ 高吞吐有序✅ 大厂标配无 TTL、无轻量级 key-value 查询为存 session 开 Kafka Topic 是杀鸡用牛刀Redis✅ 原生 hash expire✅ stream consumer group✅ Pub/Sub stream✅ 单机/集群/哨兵全支持唯一短板持久化 RDB/AOF 有丢数据风险但对 Agent 状态属可接受范围结论很清晰Redis 是唯一同时满足“亚毫秒级读写”、“原生支持多种数据结构适配不同语义”、“内置 Pub/Sub 和 Stream 两种事件模型”、“成熟稳定的集群方案”四大条件的开源系统。它不是“AI 时代的新宠”而是“AI 工程化绕不开的基建”。我见过太多团队前期用 SQLite等用户量涨到 2000 日活时突然发现 30% 的对话因 session 锁等待超时而卡死最后连夜迁移到 Redis——这种弯路本文帮你避开。2.2 MCP 协议如何放大 Redis 的不可替代性MCPModel Control Protocol的核心设计哲学是“解耦控制面与数据面”。它定义了一套标准化的 JSON-RPC 接口让 Agent 控制器Controller能以统一方式调用各种 Skill技能无论 Skill 是本地 Python 函数、远程 HTTP API 还是 Docker 容器。但协议本身不解决“谁来管这些调用的生命周期”——这正是 Redis 的舞台。具体来看 MCP 的三个关键环节如何绑定 RedisSkill 注册与发现MCP 规范要求 Controller 启动时扫描并注册所有可用 Skill。实践中我们不会把 Skill 列表硬编码在代码里而是让每个 Skill 进程启动时向 Redis 的一个hash结构如mcp:skills写入自己的元数据{ name: weather, endpoint: http://skill-weather:8000, timeout_ms: 3000, max_retries: 2 }。Controller 启动时HGETALL mcp:skills即可动态加载新增 Skill 只需起服务无需重启 Controller。这比改配置文件再发版快 10 倍。Session 与 Execution Trace 的关联存储MCP 要求每个 skill 调用必须携带session_id和execution_id。我们用 Redisstream存储完整的 trace每条消息是XADD mcp:trace * session_id sid execution_id eid skill_name weather status running timestamp ts。Stream 天然支持按时间范围查询、消费者组分发供监控服务消费、自动过期XTRIM。对比用数据库存 trace插入延迟从 12ms 降到 0.3ms且能轻松支撑每秒 5000 条 trace 写入。实时状态同步与中断控制当用户点击“停止”按钮前端发送cancel_session事件。Controller 收到后不是去遍历所有正在运行的 skill 进程可能跨机器而是向 Redis Pub/Sub 频道mcp:cancel:session_id发布一条消息。所有监听该频道的 Skill 进程通过SUBSCRIBE立即收到并优雅退出。整个过程耗时 5ms而传统 HTTP 轮询或数据库轮询至少 100ms 起。提示MCP 的wss://api.xiaozhi.me/mcp/?token...这类 endpoint本质是 MCP Server 的 WebSocket 入口。它背后必然有一个状态协调层而 Redis 是目前最轻量、最可靠的实现选择。不要被 URL 里的wss迷惑——WebSocket 只是传输层真正的状态大脑在 Redis。2.3 Python Agent 框架与 Redis 的协同范式Python 是 AI Agent 开发的绝对主力语言而 Redis-Py 是其最成熟的客户端。但很多开发者只把它当“高级字典”用这是巨大浪费。真正高效的协同是让 Python 代码的执行逻辑与 Redis 的数据结构语义深度对齐。我们以一个真实 Agent 的execute_skill方法为例import redis from redis import Redis from typing import Dict, Any, Optional class MCPAgent: def __init__(self, redis_url: str): self.redis Redis.from_url(redis_url, decode_responsesTrue) # 使用 connection pool 避免每次新建连接 self.redis.connection_pool.max_connections 50 def execute_skill(self, session_id: str, skill_name: str, params: Dict[str, Any]) - Dict[str, Any]: # 1. 从 Redis 获取 skill 元数据非阻塞 skill_meta self.redis.hgetall(fmcp:skills:{skill_name}) if not skill_meta: raise ValueError(fSkill {skill_name} not found) # 2. 生成唯一 execution_id并写入 stream记录开始 execution_id f{session_id}:{int(time.time() * 1000000)} self.redis.xadd(mcp:trace, { session_id: session_id, execution_id: execution_id, skill_name: skill_name, status: running, params: json.dumps(params), timestamp: str(time.time()) }) # 3. 设置 session 状态标记此 skill 正在执行原子操作 # 使用 hash 存储 session 状态key 为 session_idfield 为 skill_name self.redis.hset(fsession:{session_id}, skill_name, running) self.redis.expire(fsession:{session_id}, 3600) # 1小时过期 try: # 4. 实际调用 skillHTTP/本地函数等 result self._call_skill(skill_meta, params) # 5. 更新 trace 和 session 状态事务保证原子性 pipe self.redis.pipeline() pipe.xadd(mcp:trace, { session_id: session_id, execution_id: execution_id, skill_name: skill_name, status: success, result: json.dumps(result), timestamp: str(time.time()) }) pipe.hset(fsession:{session_id}, skill_name, success) pipe.execute() # 一次网络往返完成两个操作 return result except Exception as e: # 6. 失败时同样更新 trace 和 session pipe self.redis.pipeline() pipe.xadd(mcp:trace, { session_id: session_id, execution_id: execution_id, skill_name: skill_name, status: failed, error: str(e), timestamp: str(time.time()) }) pipe.hset(fsession:{session_id}, skill_name, failed) pipe.execute() raise这段代码的关键不在功能而在它如何利用 Redis 特性hsetexpire实现 session 状态的自动生命周期管理xadd写入stream天然获得时序、可回溯、可分发的能力pipeline将 trace 记录和状态更新打包为原子操作避免中间状态不一致decode_responsesTrue直接返回字符串而非 bytes省去.decode()的心智负担。注意不要在 Python 中用time.sleep()等待 Redis 操作完成。Redis 的xread、brpop等阻塞命令才是处理异步事件的正确姿势。比如 skill 进程监听mcp:cancel:session_id频道应该用redis.pubsub().subscribe()listen()循环而不是轮询。3. 核心细节解析Redis 数据建模与 Python 实操要点3.1 四大数据结构如何精准映射 AI Agent 的语义Redis 的强大源于其数据结构与业务语义的天然契合。AI Agent 的核心实体几乎都能找到最匹配的 Redis 结构AI Agent 实体推荐 Redis 结构为什么Python 操作示例Session 状态用户 ID、历史消息、当前 stepHASH支持按 field如messages,current_step独立读写HGETALL一次性获取全量EXPIRE自动过期redis.hset(session:abc123, mapping{messages: [...], current_step: 3})Skill 执行队列待执行的 skill 列表需 FIFOLISTLPUSH入队RPOP出队天然顺序支持BLPOP阻塞等待redis.lpush(queue:weather, json.dumps({city: Beijing}))Execution Trace 日志按时间排序的 skill 调用流水STREAMXADD追加XRANGE按 ID 查XREADGROUP分发给多个监控进程XTRIM自动清理旧数据redis.xadd(mcp:trace, {session_id: abc, skill: weather, status: success})Skill 元数据注册表skill 名称、地址、超时HASHHSET写入HGETALL批量读取HDEL下线HEXISTS检查存在性redis.hset(mcp:skills, weather, {endpoint:http://..., timeout:3000})特别强调STREAM的不可替代性。很多团队用LIST存 trace但LIST无法高效查询“某个 session 的所有 trace”只能LRANGE全量拉取再过滤O(n) 复杂度。而STREAM的XRANGE支持按session_id字段索引需配合XADD时的MAXLEN和FILTER实际查询延迟稳定在 0.2ms 内。我们曾将 trace 存储从LIST迁移到STREAM单日 200 万条 trace 的查询 P99 延迟从 120ms 降至 3ms。3.2 Python Redis 客户端的避坑指南连接、序列化与错误处理用好 Redis-Py远不止pip install redis那么简单。以下是我在 12 个 AI 项目中踩过的坑按优先级排序1. 连接池配置不当导致连接数爆炸默认redis.Redis()每次创建新连接高并发下瞬间打爆 Redis 连接数上限默认 10000。必须显式配置连接池# ✅ 正确复用连接限制最大数量 pool redis.ConnectionPool( hostlocalhost, port6379, db0, max_connections50, # 根据应用并发量调整通常 20-100 socket_connect_timeout1, # 连接超时 1s socket_timeout1, # 读写超时 1s retry_on_timeoutTrue # 超时自动重试 ) redis_client redis.Redis(connection_poolpool) # ❌ 错误每次 new 一个 client连接永不释放 def bad_func(): r redis.Redis() # 新连接 r.set(key, value) # 函数结束r 对象被 gc但连接可能未关闭2. 序列化方式选择影响性能与兼容性Redis 默认存 bytesPython 的dict、list直接set会报错。常见方案json.dumps()/json.loads()最通用所有语言都支持但datetime、bytes需自定义 encoder/decoderpicklePython 原生支持任意对象但绝对禁止用于跨语言或不可信数据反序列化可执行任意代码msgpack比 JSON 更快更小推荐用于内部服务间通信。# ✅ 推荐用 msgpack速度快 3 倍体积小 20% import msgpack def set_msgpack(r, key, obj): r.set(key, msgpack.packb(obj, use_bin_typeTrue)) def get_msgpack(r, key): data r.get(key) return msgpack.unpackb(data, rawFalse) if data else None # ❌ 危险用 pickle 存用户输入 r.set(user_input, pickle.dumps(user_data)) # 如果 user_data 被恶意构造反序列化时执行任意代码3. 错误处理必须区分网络异常与业务异常Redis 操作失败原因千差万别网络断开、Redis 满、key 不存在、类型错误……Python 代码必须针对性处理try: # 尝试获取 session 状态 session_data redis_client.hgetall(fsession:{session_id}) if not session_data: # 业务逻辑session 不存在可能是超时或新用户 return self._create_new_session(session_id) # 尝试执行 skill result self._execute_skill(session_data, skill_name, params) return result except redis.ConnectionError: # 网络层错误Redis 连接不上降级为本地缓存或返回友好错误 logger.error(Redis connection failed, using fallback) return self._fallback_execute(skill_name, params) except redis.TimeoutError: # 超时可能是 Redis 压力大记录告警但不中断用户 logger.warning(fRedis timeout on session {session_id}) raise TimeoutError(Service temporarily busy) except redis.ResponseError as e: # Redis 命令错误如对 string 执行 hgetall属于代码 bug需修复 logger.critical(fRedis command error: {e}) raise RuntimeError(Internal server error)注意redis.exceptions.RedisError是所有 Redis 异常的基类但捕获它会掩盖具体原因。务必按上述细分类型处理否则线上故障时你根本不知道是网络问题还是代码写错了。3.3 Redis 集群模式下的 Agent 状态一致性保障单机 Redis 能扛住 10 万 QPS但 AI Agent 场景往往需要更高可用性和容量。此时必须上 Redis Cluster。但集群带来新挑战key 的哈希槽slot分布导致 multi-key 操作受限。例如session:{id}和mcp:trace可能落在不同节点无法用pipeline原子操作。解决方案是Hash Tag用{}包裹 key 的公共部分强制相关 key 落在同一 slot。# ✅ 正确用 hash tag 确保 session 和 trace 在同一节点 session_key session:{abc123} # {abc123} 是 hash tag trace_key mcp:trace:{abc123} # 同样用 {abc123}保证和 session_key 同 slot # Redis Cluster 会只对 {} 内的内容计算 hash因此这两个 key 必定同节点 # ❌ 错误key 无 hash tag随机分布 session_key session:abc123 trace_key mcp:trace:abc123 # 可能和 session_key 不同节点pipeline 失败实测数据在 3 主 3 从的 Redis Cluster 上使用 hash tag 后sessiontrace的联合操作成功率从 82% 提升至 99.99%。更重要的是它让EVALLua 脚本成为可能——我们可以把复杂的 session 状态更新逻辑如“如果 skill A 成功则触发 skill B”写成 Lua在 Redis 端原子执行彻底避免网络往返和竞态。-- lua_script.lua: 原子更新 session 并检查触发条件 local session_key KEYS[1] -- session:{abc123} local skill_name ARGV[1] -- weather local status ARGV[2] -- success -- 更新 skill 状态 redis.call(HSET, session_key, skill_name, status) -- 检查是否所有前置 skill 都成功决定是否触发 next_skill local all_success true for _, s in ipairs({weather, news}) do if redis.call(HGET, session_key, s) ~ success then all_success false break end end if all_success then redis.call(LPUSH, queue:next_skill, summary) end return all_successPython 调用# 加载并执行 Lua 脚本 script redis_client.register_script(lua_code) result script(keys[session:{abc123}], args[weather, success])4. 实操全流程从零搭建一个 MCP Agent 的 Redis 支撑体系4.1 环境准备Docker 一键部署 Redis 集群含哨兵高可用生产环境绝不用redis-server单机启动。我们采用 Docker Compose 部署 Redis Sentinel哨兵集群兼顾简单性与高可用。以下docker-compose.yml经 3 个项目验证支持自动故障转移version: 3.8 services: redis-master: image: redis:7.2-alpine container_name: redis-master command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-master.conf:/usr/local/etc/redis.conf - ./data/master:/data ports: - 6379:6379 networks: - redis-net redis-slave1: image: redis:7.2-alpine container_name: redis-slave1 command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-slave.conf:/usr/local/etc/redis.conf - ./data/slave1:/data ports: - 6380:6379 depends_on: - redis-master networks: - redis-net redis-slave2: image: redis:7.2-alpine container_name: redis-slave2 command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-slave.conf:/usr/local/etc/redis.conf - ./data/slave2:/data ports: - 6381:6379 depends_on: - redis-master networks: - redis-net redis-sentinel1: image: redis:7.2-alpine container_name: redis-sentinel1 command: redis-sentinel /usr/local/etc/sentinel.conf volumes: - ./sentinel1.conf:/usr/local/etc/sentinel.conf ports: - 26379:26379 depends_on: - redis-master - redis-slave1 - redis-slave2 networks: - redis-net redis-sentinel2: image: redis:7.2-alpine container_name: redis-sentinel2 command: redis-sentinel /usr/local/etc/sentinel.conf volumes: - ./sentinel2.conf:/usr/local/etc/sentinel.conf ports: - 26380:26379 depends_on: - redis-master - redis-slave1 - redis-slave2 networks: - redis-net networks: redis-net: driver: bridge配套的redis-master.conf精简版port 6379 bind 0.0.0.0 protected-mode no daemonize no pidfile /var/run/redis.pid logfile dir /data dbfilename dump.rdb save 900 1 save 300 10 save 60 10000 requirepass your_strong_password_heresentinel.conf示例port 26379 bind 0.0.0.0 protected-mode no sentinel monitor mymaster redis-master 6379 2 sentinel auth-pass mymaster your_strong_password_here sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 10000 sentinel parallel-syncs mymaster 1启动命令docker-compose up -d # 等待 30 秒检查哨兵状态 docker exec -it redis-sentinel1 redis-cli -p 26379 sentinel master mymaster # 应返回类似1) ip 2) 172.20.0.2 3) port 4) 6379实操心得哨兵模式下Python 客户端不能直连redis-master而要连接哨兵由哨兵返回当前 master 地址。使用redis.sentinel.Sentinelfrom redis.sentinel import Sentinel sentinel Sentinel([(localhost, 26379), (localhost, 26380)], passwordyour_pass) master sentinel.master_for(mymaster, socket_timeout0.5) slave sentinel.slave_for(mymaster, socket_timeout0.5)4.2 Python Agent 项目初始化结构化目录与依赖管理一个可维护的 AI Agent 项目目录结构必须清晰分离关注点。我们采用以下结构已用于 5 个上线项目mcp-agent/ ├── requirements.txt # 生产依赖 ├── dev-requirements.txt # 开发依赖black, pytest ├── docker-compose.yml # 本地开发环境含 Redis、Postgres ├── src/ │ ├── __init__.py │ ├── core/ # 核心业务逻辑 │ │ ├── agent.py # MCPAgent 主类 │ │ ├── skill_registry.py # Skill 注册与发现 │ │ └── state_manager.py # Redis 状态管理封装 │ ├── skills/ # 所有 Skill 实现 │ │ ├── __init__.py │ │ ├── weather.py # 天气 Skill │ │ ├── search.py # 文档搜索 Skill │ │ └── calculator.py # 计算 Skill │ ├── models/ # 数据模型Pydantic │ │ ├── session.py │ │ └── trace.py │ └── api/ # FastAPI 接口 │ ├── __init__.py │ ├── main.py # API 入口 │ └── endpoints.py # 路由 └── tests/ # 测试 ├── test_agent.py └── test_redis.pyrequirements.txt关键依赖redis4.6.0 # 经测试最稳定的版本兼容 Redis 7 msgpack1.0.7 # 高效序列化 fastapi0.111.0 # Web 框架 uvicorn0.29.0 # ASGI 服务器 pydantic2.7.1 # 数据验证 httpx0.27.0 # HTTP 客户端比 requests 更现代注意redis4.6.0是经过大规模压测验证的版本。新版redis5.x在 pipeline 和 connection pool 上有细微行为变化曾导致我们一个项目出现 0.3% 的 pipeline 失败率回退到 4.6.0 后消失。不要盲目追新。4.3 MCP Agent 核心模块实现从 Skill 注册到 Execution Trace我们以weatherSkill 为例展示完整闭环Step 1Skill 注册启动时skills/weather.pyimport asyncio import httpx from src.core.skill_registry import register_skill register_skill(nameweather, descriptionGet current weather for a city) async def get_weather(city: str) - dict: Skill 函数接收参数返回结果 async with httpx.AsyncClient() as client: resp await client.get( fhttps://api.openweathermap.org/data/2.5/weather?q{city}appidYOUR_KEY, timeout3.0 ) resp.raise_for_status() data resp.json() return { city: city, temp_c: round(data[main][temp] - 273.15, 1), condition: data[weather][0][description] }core/skill_registry.pyfrom functools import wraps from redis import Redis SKILL_REGISTRY {} def register_skill(name: str, description: str): def decorator(func): SKILL_REGISTRY[name] { func: func, description: description, timeout_ms: 3000, max_retries: 2 } # 同时写入 Redis供 Controller 发现 redis_client Redis.from_url(redis://localhost:6379/0, decode_responsesTrue) redis_client.hset(mcp:skills, name, json.dumps({ name: name, description: description, timeout_ms: 3000, max_retries: 2 })) return func return decoratorStep 2Controller 初始化发现所有 Skillcore/agent.pyclass MCPAgent: def __init__(self, redis_url: str): self.redis Redis.from_url(redis_url, decode_responsesTrue) # 从 Redis 加载所有 Skill 元数据 self.skills {} for name, meta_json in self.redis.hgetall(mcp:skills).items(): self.skills[name] json.loads(meta_json) async def execute_skill(self, session_id: str, skill_name: str, params: dict): # ... 前面已展示的 execute_skill 实现 ...Step 3Execution Trace 写入与查询models/trace.pyfrom pydantic import BaseModel from datetime import datetime class ExecutionTrace(BaseModel): session_id: str execution_id: str skill_name: str status: str # running, success, failed params: dict result: dict None error: str None timestamp: datetime # Redis Stream 写入封装 def write_trace(redis_client: Redis, trace: ExecutionTrace): redis_client.xadd( mcp:trace, { session_id: trace.session_id, execution_id: trace.execution_id, skill_name: trace.skill_name, status: trace.status, params: json.dumps(trace.params), result: json.dumps(trace.result) if trace.result else , error: trace.error or , timestamp: trace.timestamp.isoformat() } ) # 查询某 session 的所有 trace def get_session_traces(redis_client: Redis, session_id: str) - List[ExecutionTrace]: # XRANGE 支持按字段过滤但需 Redis 7.0这里用简单 scan # 实际生产用 XRANGE MAXLEN 优化 messages redis_client.xrange(mcp:trace, count1000) traces [] for msg_id, fields in messages: if fields.get(session_id) session_id: traces.append(ExecutionTrace( session_idfields[session_id], execution_idfields[execution_id], skill_namefields[skill_name], statusfields[status], paramsjson.loads(fields[params]), resultjson.loads(fields[result]) if fields.get(result) else None, errorfields.get(error), timestampdatetime.fromisoformat(fields[timestamp]) )) return tracesStep 4API 端点暴露api/endpoints.pyfrom fastapi import APIRouter, HTTPException, BackgroundTasks from src.core.agent import MCPAgent from src.models.session import SessionCreate from src.models.trace import get_session_traces router APIRouter() router.post(/session) async def create_session(session_data: SessionCreate): # 创建新 session初始化 Redis hash session_id fsess_{int(time.time() * 1000000)} redis_client.hset(fsession:{session_id}, mapping{ created_at: str(datetime.now()), messages: json.dumps([{role: system, content: You are a helpful AI.}]) }) redis_client.expire(fsession:{session_id}, 3600) return {session_id: session_id} router.post(/session/{session_id}/skill) async def execute_skill( session_id: str
网站建设高端定制企业官网