简介:这份资源是Google第二部Agent白皮书《Agents Companion》的配套可运行源码包,面向希望将代理技术从原型Demo推进到生产部署的开发者与AI工程实践者。白皮书从概念普及转向工程化落地,围绕技术架构进阶、生态协同与标准构建、企业级落地路径及未来趋势展开,源码则帮助读者把其中的理论与策略直接落到代码层面验证。压缩包共3个文件,约9KB,以html页面、inscode配置与gitignore为主,分别承担内容展示、运行环境描述与版本忽略等用途,结构轻量、便于快速上手。目前已有136人学习下载。借助这份资料,读者可对照白皮书梳理代理技术的全生命周期操作思路,理解企业级AI应用的技术路线图与实施框架,并基于可运行源码进行学习、测试与二次开发,适合作为进阶研读与工程实践的参考起点。
1. 从一份白皮书到能跑起来的 Agent:为什么我盯着这份源码包看了三个晚上
前阵子有个做后端的朋友找我,说他们团队想试水 AI Agent,但卡在一个很尴尬的位置:概念听了一堆,真到写代码的时候,连一个能跑通的最小闭环都搭不出来。他问我有没有那种“不是纯讲道理、而是能直接跑”的东西。我把他拉到一个开源白皮书项目前——就是这份 Google Agent 白皮书配套的可运行源码包。它把 Agent 的架构拆成可执行的模块,从任务规划到工具调用再到记忆管理,每一块都有对应的代码实现,不是伪代码,不是架构图,是能git clone下来直接跑的那种。
这份资源适合两类人:一类是刚接触 Agent 概念、想通过跑通一个完整例子来建立直觉的开发者;另一类是在做 AI 应用落地、需要参考工程化实现细节的从业者。它不教你调参,也不教你训模型,它解决的是“Agent 到底怎么组织代码”这个问题。我花了三个晚上把源码包里的模块逐个拆开跑了一遍,下面把关键路径和踩过的坑整理出来。
2. 拆开源码包:Agent 的四个核心模块与依赖关系
2.1 任务规划模块:从自然语言到可执行步骤
Agent 和普通 LLM 调用最大的区别,在于它能把一个模糊的用户请求拆成有序的子任务。这份源码包里,规划模块的核心逻辑集中在planner.py这个文件里。它做的事情不复杂:接收用户输入,调用一次 LLM 做任务分解,然后把结果解析成结构化的步骤列表。
我先把最小可运行单元跑起来。源码包根目录下有一个examples/文件夹,里面放了几个场景的入口脚本。以最基础的任务规划为例:
# examples/run_planner.py from agent.planner import TaskPlanner from agent.llm_client import LLMClient # 初始化 LLM 客户端,这里用的是兼容 OpenAI 接口的配置 client = LLMClient( base_url="http://localhost:8000/v1", # 本地推理服务地址 api_key="not-needed", # 本地服务通常不校验 model="qwen2.5-7b-instruct" # 模型名称按实际部署改 ) planner = TaskPlanner(llm_client=client, max_steps=8) # 输入一个需要多步完成的任务 result = planner.plan("帮我查一下北京明天天气,如果下雨就提醒我带伞,否则提醒我涂防晒") for step in result.steps: print(f"[{step.order}] {step.action} -> {step.tool_hint}")这段代码跑通之后,输出是一个有序的步骤列表,每个步骤带一个tool_hint字段,告诉下游模块这一步大概需要什么类型的工具。max_steps这个参数控制规划的最大步数,设太小会导致复杂任务被截断,设太大又容易让 LLM 产生冗余步骤。我试下来,8 到 12 之间比较稳,具体看任务复杂度。
规划模块的 prompt 模板放在agent/prompts/planner_prompt.txt里,可以直接改。我建议第一次跑的时候先别动模板,等跑通了再根据自己场景微调。模板里有一个{tool_list}占位符,会被替换成当前注册的工具描述,这一步很关键——如果工具描述写得含糊,规划出来的步骤就会乱指工具。
2.2 工具调用模块:注册、路由与执行
工具调用是 Agent 真正“动手”的部分。源码包里用了一个注册表模式,所有工具通过装饰器注册到一个全局 registry 里,然后由ToolExecutor根据规划结果路由到具体工具。
先看工具怎么注册:
# agent/tools/weather.py from agent.tools.registry import register_tool @register_tool( name="get_weather", description="查询指定城市指定日期的天气情况", parameters={ "city": {"type": "string", "description": "城市名称"}, "date": {"type": "string", "description": "日期,格式 YYYY-MM-DD"} } ) def get_weather(city: str, date: str) -> dict: # 实际项目中这里调外部 API,示例里返回模拟数据 return { "city": city, "date": date, "condition": "rain", "temperature": "18-24C" }注册的时候有三个东西必须写清楚:name是工具的唯一标识,description会进 prompt 影响规划质量,parameters定义了参数 schema。我踩过一个坑:description写得太短,比如只写“查天气”,规划模块就经常把“查天气”和“查空气质量”搞混。后来改成“查询指定城市指定日期的天气情况,包括天气状况和温度范围”,准确率明显上来了。
工具执行器的工作流程是这样的:接收规划模块输出的步骤,从步骤里提取tool_hint,在 registry 里找到匹配的工具,然后用 LLM 从步骤的自然语言描述里抽取参数,最后调用工具函数。参数抽取这一步用的是 function calling 的格式,如果本地模型不支持 function calling,源码包里也提供了一个基于正则的 fallback 解析器,在agent/tools/parser.py里。
2.3 记忆模块:短期上下文与长期存储的边界
记忆模块是我觉得这份源码包里设计得最值得看的部分。它把记忆分成两层:短期记忆就是一个滑动窗口的对话历史,长期记忆用了一个简单的向量存储。
短期记忆的实现很直接,在agent/memory/short_term.py里,核心就是一个固定长度的 deque:
from collections import deque class ShortTermMemory: def __init__(self, max_turns=10): self.buffer = deque(maxlen=max_turns) def add(self, role: str, content: str): self.buffer.append({"role": role, "content": content}) def get_context(self) -> list: return list(self.buffer)max_turns控制保留多少轮对话。设太大,prompt 会膨胀,推理变慢;设太小,Agent 会“忘事”。我一般设 10 到 15 轮,超过这个范围的早期对话会被挤掉。如果你需要 Agent 记住更早的信息,就得靠长期记忆来补。
长期记忆在agent/memory/long_term.py,用了一个轻量的向量检索方案。每次对话结束后,把关键信息做 embedding 存进去;下一轮对话开始时,用当前输入去检索最相关的几条历史记录,拼到 prompt 里。这里有一个参数叫top_k,控制检索返回几条。我试下来,3 到 5 条比较合适,太多会引入噪声,太少又可能漏掉关键信息。
注意:长期记忆的 embedding 模型和 LLM 是分开配置的。源码包默认用了一个本地的小模型做 embedding,如果你换了 embedding 模型,记得把向量维度同步改掉,否则检索会报维度不匹配。
2.4 执行循环:Agent 的主控流程
把上面三个模块串起来的就是agent/executor.py里的主循环。逻辑不复杂:规划 → 执行 → 观察结果 → 判断是否完成 → 没完成就带着新上下文重新规划。
# agent/executor.py 核心循环(简化版) def run(self, user_input: str, max_iterations: int = 5): self.memory.short_term.add("user", user_input) for i in range(max_iterations): context = self.memory.get_full_context() plan = self.planner.plan(context) if plan.is_complete: return plan.final_answer for step in plan.steps: tool = self.registry.get(step.tool_hint) if not tool: self.memory.short_term.add("system", f"工具 {step.tool_hint} 未注册") continue result = tool.execute(step.parameters) self.memory.short_term.add("tool", str(result)) return "达到最大迭代次数,任务未完成"max_iterations是防止死循环的保险丝。我见过有人设成 20,结果 Agent 在一个查不到数据的工具上反复重试,烧了一堆 token。一般设 5 到 8 就够了,超过这个次数还没完成,大概率是规划出了问题或者工具描述有歧义,应该回去改 prompt 而不是加大循环次数。
3. 把源码跑起来:环境配置与最小闭环验证
3.1 依赖安装与模型服务对接
源码包的依赖清单在requirements.txt里,核心依赖就几个:openai用于 LLM 调用,numpy用于向量运算,pydantic用于参数校验。安装命令很标准:
# 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖 pip install -r requirements.txt # 复制配置文件模板 cp config.example.yaml config.yamlconfig.yaml里有三个关键配置段:llm段配模型服务地址和模型名,embedding段配 embedding 服务,memory段配记忆模块的参数。如果你本地没有推理服务,源码包里提供了一个mock_llm.py,可以用固定回复模拟 LLM 行为,先把流程跑通再换真实模型。
我建议第一次跑的时候用 mock 模式,确认代码路径没问题:
# 用 mock 模式跑一个完整示例 python examples/run_full_agent.py --mock --task "查一下明天天气并给出出行建议"跑通之后你会看到控制台打印出规划步骤、工具调用记录和最终回答。这个过程大概需要 30 秒,如果卡住了,大概率是某个工具的注册名和规划输出的tool_hint对不上,检查一下agent/tools/下所有工具的name字段。
3.2 接入真实模型后的参数调优
mock 模式跑通之后,把config.yaml里的llm.base_url改成你实际的推理服务地址,模型名改成你部署的模型。这里有几个参数需要根据模型能力调:
| 参数 | 作用 | 建议值 | 调整方向 |
|---|---|---|---|
temperature | 规划多样性 | 0.3 | 调高步骤更灵活但可能跑偏 |
max_tokens | 单次生成上限 | 1024 | 规划步骤多时需调大 |
top_p | 采样范围 | 0.9 | 一般不动 |
max_steps | 规划最大步数 | 8 | 复杂任务调到 12 |
max_iterations | 执行循环上限 | 5 | 调试时可临时调到 8 |
我自己的习惯是先把temperature设到 0.1,让规划尽量确定,等整个流程稳定了再慢慢往上调。有些模型在低 temperature 下会陷入重复输出,这时候加到 0.3 到 0.5 之间通常能缓解。
3.3 验证 Agent 是否真的在“用工具”
一个常见的误判是:Agent 输出了看起来合理的回答,但实际上根本没调工具,全是 LLM 自己编的。验证方法很简单,在工具函数里加一行日志:
import logging logger = logging.getLogger(__name__) @register_tool(name="get_weather", ...) def get_weather(city: str, date: str) -> dict: logger.info(f"TOOL_CALLED: get_weather | city={city} | date={date}") # ... 实际逻辑然后跑一个需要调工具的任务,看日志里有没有TOOL_CALLED。如果没有,说明规划模块没有正确识别出需要调工具,回去检查工具描述和 prompt 模板。如果有但结果不对,那就是参数抽取出了问题,看parser.py里的解析逻辑。
4. 避坑记录:跑这份源码时我踩过的五个坑
4.1 工具描述含糊导致规划乱指工具
现象:Agent 把“查天气”的任务规划成了调用“查股票”的工具,或者规划出一个根本不存在的工具名。
原因:工具注册时的description写得太短或太模糊,规划模块的 prompt 里工具列表信息量不足,LLM 只能靠猜。
解决:每个工具的description至少写清楚三件事——做什么、输入是什么、输出是什么。比如“查询指定城市指定日期的天气情况,输入城市名和日期,返回天气状况和温度范围”。改完之后重新跑规划,准确率会明显提升。
4.2 长期记忆检索返回不相关历史
现象:Agent 在回答新问题时,引用了几轮之前完全不相关的对话内容,导致回答跑偏。
原因:embedding 模型对短文本的区分度不够,或者top_k设得太大,把弱相关的记录也捞进来了。
解决:先把top_k从默认的 5 降到 3,看效果。如果还不行,检查 embedding 模型是否适合中文场景。源码包默认的 embedding 模型对中文支持一般,换成中文优化过的模型会好很多。另外,可以在检索后加一个相似度阈值过滤,低于阈值的直接丢掉。
4.3 执行循环陷入死循环烧 token
现象:Agent 反复调用同一个工具,每次返回相同结果,但就是不结束。
原因:max_iterations设得太大,或者规划模块没有正确判断任务已完成。有时候是因为工具返回的结果格式和规划模块期望的不一致,导致规划模块认为任务还没完成。
解决:先把max_iterations降到 3 做调试,观察每次循环的规划输出。如果发现规划模块反复生成相同的步骤,检查工具返回结果的格式是否和 prompt 里描述的一致。另外,可以在执行循环里加一个“相同工具连续调用超过 2 次就强制退出”的保护逻辑。
4.4 本地模型不支持 function calling 导致参数抽取失败
现象:工具被正确路由了,但参数抽取出来是空的或者格式错误。
原因:源码包默认用 function calling 格式做参数抽取,但很多本地部署的模型不支持这个特性。
解决:在config.yaml里把llm.function_calling设为false,源码包会自动切换到基于正则的 fallback 解析器。这个解析器对格式要求比较严格,规划模块输出的步骤描述需要遵循“动作 + 参数名=参数值”的格式。如果还是解析失败,可以在parser.py里加自定义的解析规则。
4.5 记忆窗口溢出导致早期关键信息丢失
现象:多轮对话后,Agent 忘记了最初用户说的关键约束条件。
原因:短期记忆的max_turns设得太小,早期对话被挤出窗口;同时长期记忆没有及时存入关键信息。
解决:把max_turns调到 15 到 20,同时在每轮对话结束后,手动把关键约束写入长期记忆。源码包里提供了一个memory.mark_important()方法,可以把指定内容强制存入长期存储,不受窗口限制。我一般会在用户输入里检测“必须”“不要”“一定要”这类关键词,命中就标记为重要信息。
5. 进阶用法:把 Agent 接到真实业务流里的两个技巧
5.1 用工具链组合替代单步规划
源码包默认的规划模式是“一次性规划所有步骤”,但实际业务里,很多任务的后续步骤依赖前一步的具体返回值,没法提前规划。比如“查库存,如果低于阈值就下单补货”,下单这个动作是否执行取决于查库存的结果。
我的做法是把这种条件逻辑封装成一个组合工具,在工具内部做判断,而不是让规划模块去处理条件分支:
@register_tool( name="check_and_replenish", description="查询库存,低于阈值时自动触发补货流程", parameters={ "sku": {"type": "string", "description": "商品编码"}, "threshold": {"type": "integer", "description": "库存阈值"} } ) def check_and_replenish(sku: str, threshold: int) -> dict: stock = query_stock(sku) if stock < threshold: order_id = create_replenishment_order(sku, threshold * 2) return {"action": "replenished", "order_id": order_id, "current_stock": stock} return {"action": "no_action", "current_stock": stock}这样规划模块只需要规划一步“调用 check_and_replenish”,条件判断在工具内部完成。好处是规划逻辑简单、不容易出错;代价是灵活性降低,如果业务规则变了,得改工具代码而不是改 prompt。我一般把稳定的业务规则封装成组合工具,把多变的策略留给规划模块。
5.2 用执行日志做离线评估
Agent 跑起来之后,怎么知道它到底靠不靠谱?我的习惯是每次执行都落一份结构化日志,记录规划步骤、工具调用、参数、返回结果和最终回答。然后定期抽样人工评估,看规划是否合理、工具调用是否正确、最终回答是否满足用户意图。
源码包里有一个agent/logger.py,默认把日志写到logs/agent_run.jsonl。我在此基础上加了一个评估脚本,统计几个指标:工具调用准确率(规划的工具和实际需要的工具是否一致)、参数抽取准确率、任务完成率。这三个指标能覆盖大部分问题。如果工具调用准确率低于 80%,优先改工具描述;如果参数抽取准确率低,优先改 parser 或换模型;如果任务完成率低但前两个指标正常,那就是规划策略的问题,需要调 prompt 或调整max_steps。
从那以后我每次接入新的 Agent 项目,都会先把日志和评估脚本搭好再跑业务逻辑,不然出了问题连从哪里查都不知道。希望这份拆解能帮到你,少走几个我踩过的弯路。
本文还有配套的精品资源,点击获取