ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Redis作为AI Agent状态总线的核心实践

2026/10/2 22:16:25 拓冰建站 浏览量
Redis作为AI Agent状态总线的核心实践 1. 项目概述Redis 并没有“正式接入 AI”但这件事正在真实发生“Redis 已正式接入 AI”——看到这个标题我第一反应是点开确认是不是 Redis 官方博客发了公告或者 RedisConf 上公布了什么重磅集成。结果搜了一圈Redis Labs 官网、GitHub 主仓库、官方博客、Twitter/X 账号全都没有任何关于“AI 原生支持”或“内置 AI 引擎”的正式声明。Redis 还是那个 Redis一个以内存速度读写、以数据结构丰富著称、以稳定性和低延迟见长的键值存储系统。它没有突然长出推理能力也没有内置大模型调度器。那这个标题到底在说什么结合你提供的热搜词——Redis、AI、MCP、agent-skills、Python再叠加当前技术一线的真实动向答案很清晰这不是 Redis 自身的升级而是Redis 正在成为 AI Agent 架构中不可或缺的“记忆中枢”与“状态总线”。准确地说是开发者群体正大规模、高密度地将 Redis 用作 AI Agent 系统的底层支撑设施尤其在 MCPModel Control Protocol协议生态中Redis 扮演着核心协调角色。所谓“接入 AI”本质是 Redis 的能力边界被重新定义它不再只是缓存或会话存储而成了 Agent 的短期记忆库、技能调用队列、工具执行上下文、多步任务状态快照中心。为什么是 Redis不是 PostgreSQL不是 MongoDB更不是自建内存哈希表因为它的原子性操作 发布订阅 流式数据结构 高吞吐低延迟 成熟的分布式方案恰好严丝合缝地匹配了 AI Agent 的运行范式。比如一个 Agent 要连续调用天气 API → 解析 JSON → 写入数据库 → 生成摘要 → 发送邮件这中间每一步的状态、中间结果、错误回滚点、并发锁控制全都可以用 Redis 的STREAM存事件日志、用HASH存任务上下文、用ZSET排队待执行技能、用PUB/SUB实现模块间解耦通信。这不是“AI 功能嫁接”而是基础设施层对新型计算范式的深度适配。适合谁看如果你正在用 LangChain、LlamaIndex 或自研框架构建 Agent 应用如果你在调试 MCP 协议下的工具调用失败问题如果你发现 Agent 在多步任务中状态丢失、技能串行变乱序、重试逻辑失控——那么这篇内容就是为你写的。它不讲虚概念只拆真实链路、列实操配置、曝踩坑现场。下面我们就从设计逻辑开始一层层剥开 Redis 在 AI Agent 架构中的真实角色。2. 核心设计思路为什么 Redis 是 AI Agent 的“隐形操作系统”2.1 不是功能叠加而是范式迁移从“存储”到“状态总线”传统 Web 应用里Redis 的角色很明确缓存热点数据、存 Session、做计数器、实现分布式锁。它的价值在于“快”和“轻”。但在 AI Agent 场景下它的价值发生了质变——它成了整个 Agent 运行时的状态总线State Bus。这个概念需要具象化理解想象一个自动驾驶汽车的中央控制器它不负责识别红绿灯那是视觉模型的事也不负责规划路径那是规划模块的事但它必须实时汇总摄像头帧、雷达点云、GPS 坐标、转向指令、刹车压力并确保所有子系统看到的是同一份“此刻车况”。Redis 就是 Agent 的这个中央控制器。举个具体例子一个基于 MCP 协议的代码审查 Agent收到用户提交的 PR 后要依次执行从 Git 仓库拉取 diff 内容Tool:git_fetch_diff调用代码分析模型提取潜在 bugTool:code_analyzer调用风格检查器验证 PEP8Tool:style_checker汇总所有结果生成 Markdown 评论Tool:comment_generator这四步不是简单线性执行。第 2 步可能因 token 超限需分块处理第 3 步可能因代码量过大触发异步扫描第 4 步需要等待前两步全部完成才能启动。如果用传统方式你得自己维护一个状态机用数据库记录每个步骤的 status、input、output、error、retry_count……而用 Redis你可以这样设计用一个HASH键agent:pr_review:12345:state存储整个任务的元信息{status: running, step: 2, retry_count: 0}用STREAM键agent:pr_review:12345:events记录所有动作事件{type: tool_call, tool: git_fetch_diff, input: ..., ts: 1717023456}用LIST键agent:pr_review:12345:pending_tools存放待执行技能[code_analyzer, style_checker]用PUB/SUB频道agent:pr_review:12345:control发送控制指令{cmd: pause, reason: rate_limit}所有这些操作Redis 都能在微秒级完成且天然支持原子性如HINCRBY更新 retry_count LPUSH入队新任务、事务性MULTI/EXEC保证状态一致性、广播性PUBLISH通知所有监听 worker。这才是它不可替代的核心——不是它会 AI而是它让 AI 的复杂协作变得可管理、可追溯、可伸缩。2.2 为什么不是其他数据库对比 PostgreSQL、MongoDB、SQLite 的硬伤有人会问既然要存状态用 PostgreSQL 不是更可靠用 MongoDB 不是更灵活甚至用内存字典不更简单我们来逐一对比看 Redis 的不可替代性在哪维度RedisPostgreSQLMongoDB内存字典Python dict读写延迟 100μs本地~1ms网络SQL解析~2-5msJSON序列化网络 1μs但仅限单进程原子操作INCR,HSETNX,LPOP/RPUSH原生支持需SELECT FOR UPDATE 事务复杂且慢findAndModify支持有限原子更新无需threading.Lock无法跨进程发布订阅原生PUB/SUB毫秒级广播需LISTEN/NOTIFY依赖 pg_notify配置复杂无原生 pub/sub需第三方插件或轮询无需自建消息队列流式处理XADD/XREAD天然支持事件流、消费者组需物化视图触发器模拟性能差Change Stream有但延迟高、资源消耗大无需手动维护队列分布式锁SET key val NX PX 10000 Lua 脚本成熟可靠pg_advisory_lock可用但语义不同易死锁findAndModify模拟但无自动过期易残留锁无法跨进程完全失效关键结论AI Agent 的核心瓶颈从来不是存储容量而是状态变更的实时性、并发安全性、跨服务通信效率。PostgreSQL 的 ACID 是为金融交易设计的它的强一致性在 Agent 场景下是过度设计反而拖慢响应MongoDB 的文档灵活性在 Agent 的结构化状态面前并无优势而内存字典连进程隔离都做不到根本无法支撑生产级 Agent 集群。Redis 的设计哲学——“极简数据结构 极致性能 极小运维开销”——恰恰是 AI Agent 这种高动态、低延迟、多实例协同场景的完美匹配。2.3 MCP 协议下的 Redis 定位Agent-Skills 的“技能调度中心”MCPModel Control Protocol是一个新兴的、旨在标准化 AI Agent 与外部工具交互的协议。它定义了一套 JSON-RPC 风格的接口让 Agent 能统一调用天气、日历、代码仓库、数据库等各类工具。而 Redis 在其中的角色远不止是存个调用日志那么简单。它是整个 MCP 生态的技能调度中心Skill Orchestrator。具体怎么运作一个符合 MCP 规范的工具比如mcp-server-git启动后会向 Redis 注册自身能力# 工具注册命令伪代码 redis-cli HSET mcp:tools:git name git_fetch_diff description Fetch git diff for a PR schema {repo: string, pr_id: int}Agent 在需要调用时不是直接 HTTP 请求工具服务而是先查 Redis# Python 伪代码 tool_info redis.hgetall(mcp:tools:git) if tool_info and tool_info.get(status) online: # 生成唯一 task_id task_id str(uuid.uuid4()) # 将调用请求推入工具专属队列 redis.lpush(fmcp:queue:git_fetch_diff, json.dumps({ task_id: task_id, params: {repo: myorg/app, pr_id: 123}, callback_url: http://agent:8000/callback })) # 设置任务状态为 pending redis.hset(fmcp:task:{task_id}, mapping{status: pending, created_at: time.time()})工具服务端则持续监听对应队列# 工具服务端伪代码使用 redis-py while True: task_data redis.brpop(mcp:queue:git_fetch_diff, timeout1) if task_data: task json.loads(task_data[1]) result execute_git_fetch(task[params]) # 更新任务状态并推送结果 redis.hset(fmcp:task:{task[task_id]}, mapping{ status: completed, result: json.dumps(result), finished_at: time.time() }) requests.post(task[callback_url], json{task_id: task[task_id]})这个模式带来的好处是颠覆性的解耦Agent 和工具无需知道对方网络地址只认 Redis弹性工具可以水平扩缩容Redis 队列自动负载均衡可观测所有任务状态、耗时、失败率通过HGETALLKEYS mcp:task:*一键获取重试可控失败任务可LINSERT回队列头部或HSET修改retry_count后重入审计合规STREAM记录所有调用事件满足安全审计要求。这才是“Redis 接入 AI”的真实含义——它成了 MCP 协议落地的事实标准中间件而非某个功能模块。3. 核心细节解析Redis 在 AI Agent 中的四大关键数据结构实战3.1 HASHAgent 任务状态的“黄金存储单元”在 AI Agent 开发中最常被滥用又最易出错的数据结构就是任务状态存储。新手常犯的错误是把整个任务对象序列化成 JSON 存进一个 String 键里然后每次读写都全量加载、修改、覆盖。这在单步任务中尚可一旦涉及多步、重试、并发就会出现状态覆盖、丢失更新、竞态条件等问题。正确做法是用 RedisHASH。它天然支持字段级原子操作完美匹配任务状态的多属性特性。一个典型的 Agent 任务状态 HASH 结构如下# 键名agent:task:{task_id} # 字段示例 HSET agent:task:abc123 \ status running \ current_step code_analyzer \ input {repo:myorg/app,pr_id:123} \ output \ error \ retry_count 0 \ created_at 1717023456 \ updated_at 1717023456 \ last_heartbeat 1717023456为什么 HASH 是最优解字段级原子更新HINCRBY agent:task:abc123 retry_count 1可安全递增重试次数无需担心并发覆盖部分读取HMGET agent:task:abc123 status current_step output只取需要的字段减少网络传输存在性判断HEXISTS agent:task:abc123 error快速判断是否出错比HGETNone判断更高效过期控制EXPIRE agent:task:abc123 3600设置 1 小时过期避免僵尸任务堆积。实操注意事项提示不要用HGETALL获取全部字段再 Python 里处理。HGETALL返回所有字段即使你只关心status也会浪费带宽。务必用HMGET指定字段。注意HSET的字段名不要包含空格或特殊字符。推荐用下划线命名法current_step避免current step这类导致命令解析失败。警告HDEL删除字段时如果字段不存在Redis 不报错但返回 0。务必检查返回值否则可能误判删除失败。我在线上环境踩过一次坑一个任务因超时被标记为failed但后续重试逻辑错误地执行了HDEL agent:task:abc123 error结果error字段被删status却还是failed导致监控系统漏报。后来改成统一用HSET更新所有相关字段杜绝部分更新。3.2 STREAMAgent 事件溯源的“不可篡改账本”AI Agent 的行为必须可追溯、可审计、可重放。当用户投诉“为什么我的代码审查没生成评论”你不能只查最终状态而要还原整个决策链哪一步调用失败哪个模型返回了空结果哪次重试超时了这就是STREAM的用武之地。STREAM是 Redis 的持久化日志结构支持多消费者组、消息 ID 自增、阻塞读取。一个 Agent 事件流的设计如下# 事件流键名agent:events:{task_id} # 消费者组名agent-consumer-group # 示例事件使用 XADD XADD agent:events:abc123 * \ type tool_call \ tool git_fetch_diff \ params {repo:myorg/app,pr_id:123} \ ts 1717023456.123 XADD agent:events:abc123 * \ type tool_result \ tool git_fetch_diff \ result [file1.py:10,-5, file2.js:2] \ status success \ ts 1717023456.456STREAM 的核心优势严格时序消息 ID 格式为毫秒时间戳-序号如1717023456123-0天然保证事件顺序多消费者监控服务、审计服务、重放服务可各自创建独立消费者组互不影响断点续读XREADGROUP GROUP agent-consumer-group consumer-1 COUNT 10 STREAMS agent:events:abc123 中的表示读取未分配消息$表示读取最新消息1717023456123-0表示从指定 ID 开始灵活应对各种场景自动清理XTRIM agent:events:abc123 MAXLEN 1000限制最多保留 1000 条防止无限增长。实操心得我最初用XADD时习惯性给每条消息加一个业务 ID如event_id: uuid4()后来发现这是冗余的。Redis 的消息 ID 本身已是全局唯一且有序额外加event_id字段不仅浪费空间还增加序列化开销。现在所有事件都只用 Redis 原生 ID业务层通过XRANGE查询时用ID字段做关联即可。另一个教训XREADGROUP默认会把读取的消息标记为pending如果消费者崩溃未XACK这些消息会一直卡在 pending 列表里。线上曾因此导致大量消息积压监控报警。解决方案是设置合理的TIMEOUT如XREADGROUP ... TIMEOUT 30000并定期用XPENDING清理超时 pending 消息。3.3 PUB/SUBAgent 模块间的“零延迟神经突触”在复杂 Agent 系统中模块间通信不能依赖 HTTP 轮询或数据库轮询那太慢、太耗资源。PUB/SUB提供了真正的事件驱动通信延迟低于 1ms是模块解耦的终极方案。典型应用场景控制指令下发Agent Manager 向所有 Worker 发送pause、resume、scale_up指令状态广播某个 Worker 完成关键步骤广播step_completed:code_analyzer触发下游模块启动异常告警工具调用失败时发布alert:tool_failure:git_fetch_diff由告警服务统一处理。# 发布控制指令 PUBLISH agent:control:all {cmd: pause, reason: maintenance} # Worker 订阅 SUBSCRIBE agent:control:all # 发布状态变更 PUBLISH agent:state:abc123 {status: step_completed, step: code_analyzer} # 订阅特定任务状态 PSUBSCRIBE agent:state:* # 通配符订阅PUB/SUB 的关键特性即发即弃消息不持久化订阅者必须在线才能接收。这恰合控制指令场景——离线 Worker 重启后自然恢复无需处理历史指令通配符订阅PSUBSCRIBE agent:state:*让一个 Worker 同时监听所有任务状态避免为每个任务单独 SUBSCRIBE轻量高效相比 Kafka 或 RabbitMQPUB/SUB零配置、零依赖启动即用。避坑指南注意PUB/SUB是“发后即忘”没有 ACK 机制。如果需要确保指令送达必须配合HASH状态存储。例如Manager 发布pause后应立即HSET agent:worker:worker-01 status pausingWorker 收到消息后HSET自己状态为pausedManager 定期检查所有 Worker 状态对超时未更新的主动干预。警告不要在PUB/SUB中传递大消息1KB。Redis 的PUB/SUB是内存复制大消息会阻塞主线程。超过 1KB 的数据应存入STRING或HASH只在PUB/SUB中传递键名。我曾在一个高并发测试中误将 50KB 的模型输出 JSON 通过PUBLISH发送结果整个 Redis 实例 CPU 爆到 100%所有命令延迟飙升。后来改为只发task_id接收方用HGET获取详情瞬间恢复。3.4 ZSETAgent 技能队列的“智能优先级调度器”当 Agent 需要并发执行多个技能如同时调用天气、股票、新闻 API或需要按优先级调度VIP 用户任务 普通用户任务简单的LIST队列就力不从心了。ZSET有序集合凭借其分数score排序能力成为智能调度的基石。一个技能调度ZSET的设计# 键名agent:skill_queue # 成员格式{task_id}:{tool_name} # 分数score调度优先级数值越小越优先 ZADD agent:skill_queue 100 abc123:git_fetch_diff ZADD agent:skill_queue 200 def456:code_analyzer ZADD agent:skill_queue 50 xyz789:style_checker # VIP 任务分数最低最先执行ZSET 调度策略示例静态优先级VIP 用户任务score10普通用户score100后台任务score1000动态优先级根据任务创建时间计算score time.time() (wait_time_in_seconds * 10)越早创建越优先混合优先级score (user_priority * 1000) (time.time() * 1)兼顾用户等级和等待时长。实操技巧用ZRANGE agent:skill_queue 0 0 WITHSCORES获取最高优先级任务用ZREM agent:skill_queue abc123:git_fetch_diff移除已处理任务用ZCOUNT agent:skill_queue 0 100统计 VIP 任务数量用ZREVRANGEBYSCORE agent:skill_queue inf 100 LIMIT 0 10获取所有优先级 ≤100 的任务倒序最高优在前。我在线上用过一个巧妙的“饥饿度”算法初始score time.time()每次重试score score - 1000负数表示更饿确保失败任务会越来越优先。配合ZREMRANGEBYSCORE agent:skill_queue -inf -1000000定期清理超低分僵尸任务效果极佳。4. 实操过程从零搭建一个 MCP 兼容的 Redis Agent 基础架构4.1 环境准备Docker 一键部署高可用 Redis 集群生产环境绝不能用单节点 Redis。AI Agent 对状态一致性要求极高单点故障会导致整个任务流中断。我们采用 Redis Cluster 模式6 节点3 主 3 从通过 Docker Compose 快速搭建。# docker-compose.yml version: 3.8 services: redis-node-1: image: redis:7.2-alpine container_name: redis-node-1 command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-node-1.conf:/usr/local/etc/redis.conf - ./data/node-1:/data ports: - 7001:6379 - 17001:16379 networks: - redis-net redis-node-2: image: redis:7.2-alpine container_name: redis-node-2 command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-node-2.conf:/usr/local/etc/redis.conf - ./data/node-2:/data ports: - 7002:6379 - 17002:16379 networks: - redis-net # ... 定义 redis-node-3 至 redis-node-6省略结构相同 redis-cluster-create: image: redis:7.2-alpine container_name: redis-cluster-create depends_on: - redis-node-1 - redis-node-2 - redis-node-3 - redis-node-4 - redis-node-5 - redis-node-6 command: sh -c sleep 10 redis-cli --cluster create 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379 --cluster-replicas 1 --cluster-yes networks: - redis-net关键配置文件redis-node-1.confport 6379 bind 0.0.0.0 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes appendfilename appendonly.aof save 部署步骤创建docker-compose.yml和 6 个redis-node-X.conf文件mkdir -p data/{node-1..node-6}创建数据目录docker-compose up -d启动所有容器docker logs redis-cluster-create等待集群创建完成约 30 秒验证docker exec -it redis-node-1 redis-cli -c cluster info查看cluster_state:ok。为什么选 Redis 7.2原生支持Redis FunctionsLua 2.0可编写复杂原子操作STREAM增强的XAUTOCLAIM更好处理 pending 消息更好的内存碎片控制对长期运行的 Agent 服务至关重要。4.2 Python Agent SDK封装 Redis 操作的轻量级工具包直接裸写redis-py命令既繁琐又易错。我们封装一个RedisAgentSDK隐藏底层细节暴露语义化接口。# redis_agent_sdk.py import redis import json import uuid from typing import Dict, Any, Optional, List class RedisAgentSDK: def __init__(self, hostlocalhost, port6379, db0, passwordNone): self.client redis.Redis( hosthost, portport, dbdb, passwordpassword, decode_responsesTrue, # 自动解码 bytes - str socket_connect_timeout2, socket_timeout2 ) # 预热连接 try: self.client.ping() except redis.ConnectionError: raise ConnectionError(Failed to connect to Redis) def create_task(self, task_id: str, input_data: Dict[str, Any]) - bool: 创建新任务初始化状态 key fagent:task:{task_id} data { status: created, input: json.dumps(input_data), output: , error: , retry_count: 0, created_at: str(time.time()), updated_at: str(time.time()) } return self.client.hset(key, mappingdata) len(data) def update_task_status(self, task_id: str, status: str, **kwargs) - bool: 更新任务状态支持任意字段 key fagent:task:{task_id} update_data {status: status, updated_at: str(time.time())} update_data.update({k: json.dumps(v) if isinstance(v, (dict, list)) else str(v) for k, v in kwargs.items()}) return self.client.hset(key, mappingupdate_data) 0 def get_task_state(self, task_id: str, fields: List[str] None) - Dict[str, Any]: 获取任务状态支持部分字段 key fagent:task:{task_id} if fields is None: raw self.client.hgetall(key) else: raw self.client.hmget(key, fields) raw dict(zip(fields, raw)) # 自动反序列化 JSON 字段 for field in [input, output, error]: if field in raw and raw[field]: try: raw[field] json.loads(raw[field]) except json.JSONDecodeError: pass return raw def log_event(self, task_id: str, event_type: str, **kwargs) - str: 记录事件到 STREAM stream_key fagent:events:{task_id} event {type: event_type, ts: str(time.time())} event.update(kwargs) return self.client.xadd(stream_key, event) def publish_control(self, channel: str, message: Dict[str, Any]): 发布控制指令 self.client.publish(channel, json.dumps(message)) # 使用示例 sdk RedisAgentSDK(hostredis-node-1, port6379) task_id str(uuid.uuid4()) sdk.create_task(task_id, {repo: myorg/app, pr_id: 123}) sdk.update_task_status(task_id, running, current_stepgit_fetch_diff) sdk.log_event(task_id, tool_call, toolgit_fetch_diff, params{repo: myorg/app})SDK 设计哲学自动 JSON 序列化/反序列化开发者传 Python dictSDK 自动处理避免手动json.dumps连接池复用redis-py默认启用连接池decode_responsesTrue避免b...字节串烦恼超时防护socket_connect_timeout和socket_timeout防止 Redis 挂掉时整个 Agent 卡死幂等性保障create_task返回布尔值update_task_status检查更新字段数便于重试逻辑。4.3 MCP Tool Server用 Python 实现一个可注册的 Git 工具服务MCP 协议要求工具服务能向中央注册并监听任务队列。我们用 Flask redis-py 实现一个最小可行的mcp-server-git。# mcp_git_server.py from flask import Flask, request, jsonify import redis import json import subprocess import os import logging app Flask(__name__) redis_client redis.Redis(hostredis-node-1, port6379, decode_responsesTrue) # 工具注册 TOOL_INFO { name: git_fetch_diff, description: Fetch git diff for a PR, schema: { type: object, properties: { repo: {type: string}, pr_id: {type: integer} }, required: [repo, pr_id] } } app.before_first_request def register_tool(): 服务启动时向 Redis 注册自身 redis_client.hset(mcp:tools:git_fetch_diff, mappingTOOL_INFO) redis_client.hset(mcp:tools:git_fetch_diff, status, online) logging.info(Git tool registered to Redis) app.route(/health, methods[GET]) def health(): return jsonify({status: ok}) app.route(/execute, methods[POST]) def execute(): MCP 协议执行端点供 Agent 直接调用非队列模式 data request.get_json() repo data.get(repo) pr_id data.get(pr_id) try: # 模拟 git 命令实际应调用真实 git result fdiff for {repo} PR#{pr_id}: 10 lines, -5 lines return jsonify({result: result, status: success}) except Exception as e: return jsonify({error: str(e), status: error}), 500 # 后台任务监听器独立线程 def listen_to_queue(): while True: # 阻塞式监听队列超时 5 秒 task_data redis_client.brpop(mcp:queue:git_fetch_diff, timeout5) if task_data: task json.loads(task_data[1]) try: # 执行实际逻辑 result fdiff for {task[params][repo]} PR#{task[params][pr_id]} # 更新任务状态 redis_client.hset(fmcp:task:{task[task_id]}, mapping{ status: completed, result: json.dumps({diff: result}), finished_at: str(time.time()) }) # 发送回调 requests.post(task[callback_url], json{ task_id: task[task_id], status: completed, result: {diff: result} }) except Exception as e: redis_client.hset(fmcp:task:{task[task_id]}, mapping{ status: failed, error: str(e), finished_at: str(time.time()) }) if __name__ __main__: from threading import Thread # 启动队列监听器 t Thread(targetlisten_to_queue, daemonTrue) t.start() app.run(host0.0.0.0, port5001)部署与验证pip install flask redis requests安装依赖python mcp_git_server.py启动服务查看 Redisredis-cli HGETALL mcp:tools:git_fetch_diff确认注册成功手动推送测试任务redis-cli LPUSH mcp:queue:git_fetch_diff {task_id:test123,params:{repo:test,pr_id:1},callback_url:http://localhost:8000/callback}观察服务日志确认任务被消费并回调。4.4 Agent Core一个三步式代码审查 Agent 的完整实现最后我们整合所有组件实现一个真实的、可运行的 Agent。它接收用户输入调用 Git 工具调用代码分析模型此处用 mock生成评论。# agent_core