news 2026/9/25 23:17:14

搭建Agent系统实战:从0到1把大模型变成能干活的手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
搭建Agent系统实战:从0到1把大模型变成能干活的手

简介:一份面向软件开发者的Agent系统可运行源码包,适合具备Python基础、希望从零搭建或升级Agent应用的读者。资源完整呈现从离线笔记到联机版Agent的升级实践,涵盖Researcher、Editor、Note Taker三个角色的分工协作,以及搜索工具、笔记工具、AI搜索与报告生成等关键功能的代码实现。压缩包共9个文件,以4个Python脚本为核心,配合依赖清单、环境变量示例、运行说明文档,压缩后仅15KB,结构紧凑便于快速运行与二次开发。已有150人学习下载,资源中的代码和案例演示可帮助读者直观理解Agent工作原理与RAG应用方式,适合在真实项目中动手验证并继续扩展。

1. 搭建Agent系统指南:从0到1把大模型变成能干活的手

这份「搭建Agent系统指南[可运行源码]」我拆了两遍,第一遍看的是热闹,第二遍才看出门道。它不是什么教科书式的理论讲义,而是一套从零开始搭Agent系统的可运行源码工程,把大模型、工具调用、记忆管理和任务规划四个环节串成一条完整链路。很多人对大模型Agent的印象停留在「聊天机器人加个提示词」,实际上能跑起来的Agent系统要处理的是函数调用、上下文裁剪、工具注册、循环执行上限这些工程细节,单个环节看着不难,串起来全是坑。这套源码适合两类人:一类是想把大模型接进业务系统但没摸过Agent编排的Java或Python后端工程师,另一类是已经在用LangChain但觉得黑匣子太重、想自己掌控每一步的算法工程师。接下来我按「框架选型 → 环境跑通 → 核心模块 → 部署避坑 → 进阶改造」的顺序拆,全程都是能直接照抄的代码和可复现的步骤。

2. 为什么不用LangChain硬套:Agent框架选型与核心机制

2.1 Agent系统到底在解决什么问题

先理清一个概念。大模型本身没有自主行动能力,你问它「帮我把这个目录下的文件名整理成表格」,它只能给你一段Markdown文本,不会真的去读文件系统。Agent系统的本质是给大模型装上「手」和「眼睛」:通过工具注册让模型能调用外部函数,通过循环执行让模型能分步完成任务,通过记忆管理让模型能记住上下文。这套源码里Agent的行为模式是一个标准的ReAct循环——模型先思考(Reason),再决定调用哪个工具(Act),拿到工具返回结果后继续思考,直到满足终止条件。

这个设计直接决定了源码的结构。整个工程划分成四层:模型层负责封装不同厂商的LLM接口,工具层维护一组可被调用的函数注册表,记忆层管理短时对话和长时向量记忆,编排层把前三者串成循环。这和LangChain的设计哲学完全不同——LangChain把每一步都抽象成Chain,灵活性高但调试时要追的堆栈很深,而这套源码选择把循环逻辑摊开写成显式代码,好处是每一步的状态都能清清楚楚地打印出来,坏处是你要自己处理很多边界情况。但这正是「系统课」该有的样子:你拿到的不只是能跑的东西,而是能看懂的骨架。

2.2 选型对比:什么时候该自建,什么时候该用框架

选型这件事我踩过不止一次。早期做Agent原型时图省事直接用LangChain,写Prompt模板确实快,但到了要自定义工具返回格式、精细化控制模型重试策略的时候,框架的抽象层反而成了阻碍。这套源码的路线是「轻依赖」:只依赖OpenAI SDK兼容的HTTP接口和少量工具库,没有引入重型编排框架。

我列一个自建与框架的对比,方便你判断场景:

维度自建编排(本源码路线)LangChain/LlamaIndex
循环控制显式while,每步可见隐式AgentExecutor,内部逻辑黑匣子
工具注册字典+装饰器,代码量约30行需理解Tool基类和参数schema转换
依赖数量核心依赖5个左右连带依赖经常超过40个
调试体验打印每一步thought/action即可要开verbose或debug回调
换模型厂商改一个base_url和key要适配对应的LangChain集成包
适合场景理解原理、生产定制、轻量部署快速验证、社区生态依赖

这套源码我判定为「半自建」路线——对话轮次管理、人机交互接口这些通用部分自己写,但模型接入用的是兼容OpenAI格式的SDK,这意味着你可以无缝切换DeepSeek、通义千问、Kimi这类提供OpenAI兼容接口的服务。实际在生产项目里,我现在的习惯也是「能用OpenAI协议就不引入厂商私有SDK」,因为一旦某个模型服务出问题,换备用的成本只是改环境变量。

2.3 这套源码的目录结构与模块职责

拿到源码包后第一件事不是看代码,而是先搞清目录职责。整体结构是一个标准的Python工程,入口文件、核心逻辑、工具集合、配置管理分开存放。源码里没有用复杂的包管理工具,一个requirements.txt就能装完所有依赖,这对我这种习惯在服务器上直接跑的人来说友好很多,不用折腾poetry或uv。

核心模块的职责我拆成三块看:模型封装模块负责把不同厂商的API差异屏蔽掉,统一输出文本和token消耗统计;工具模块是Agent的「手」,每个工具函数都有明确的名称、描述、参数schema,模型通过JSON格式决定调用谁;编排模块是大脑,维护对话历史、调用模型、解析返回、决定是否继续循环。这三个模块互相独立、单向依赖,改工具不需要动编排逻辑,这点对二次开发很重要——如果你想加一个新工具,流程就是写一个普通Python函数,配上描述和参数说明,注册进工具表,Agent立刻就能用。

3. 把可运行源码跑起来:环境准备、模型接入与最小启动

3.1 环境准备与依赖安装

环境这块直接说明我的实测结论:Python 3.10及以上版本都能跑,3.9以下会碰上类型语法兼容问题,不建议。操作系统上Windows、macOS、Linux都能正常装依赖,但如果你用的是Windows,建议全程在WSL2里跑,因为后续如果要接本地模型或部署Docker镜像,WSL2的坑比Windows原生环境少一半。装依赖的命令很简单:

cd agent-system python -m venv venv source venv/bin/activate # Windows下用 venv\Scripts\activate pip install -r requirements.txt

这里我解释一下为什么用python -m venv而不是直接用conda:这套源码的依赖列表里有一些针对特定Python版本编译的包,conda默认源偶尔会解析到错误的版本组合,而venv直接用pip解析,能严格按requirements.txt里锁定的版本走。安装完依赖后,验证安装是否完整的方法是直接尝试导入核心模块:

python -c "from agent.core import orchestrator; print('core ok')" python -c "from agent.tools import registry; print('tools ok')"

正常输出两个ok说明依赖安装干净。

3.2 模型服务接入:OpenAI兼容接口统一配置

这套源码有个明显特征:模型接入不绑定任何一家厂商SDK,而是统一走OpenAI兼容的HTTP协议。这意味着你只需要配置三个变量就能换一家模型服务:base_url、api_key、model_name。配置文件里长这样:

# config.py 核心配置段 MODEL_CONFIG = { "default": { "base_url": "https://api.openai.com/v1", "api_key": "${OPENAI_API_KEY}", "model_name": "gpt-4o-mini", "temperature": 0.3, "max_tokens": 2048, "timeout": 60 }, "fast": { "base_url": "https://api.deepseek.com/v1", "api_key": "${DEEPSEEK_API_KEY}", "model_name": "deepseek-chat", "temperature": 0.1, "max_tokens": 1024, "timeout": 30 } }

这段配置的关键点是base_url必须精确到/v1,如果漏掉后缀,模型接口会直接报404。fast配置是我建议单独保留的——Agent的循环过程中有大量「判断下一步做什么」的轻量调用,用快速模型能省一半以上的token成本,只有真正需要生成最终答案时才切回default模型。这套源码在编排层已经支持按调用类型选模型,这是很多人容易忽略但省钱效果明显的设计。

配置好模型后,用一个简单的对话测试验证链路通畅:

from agent.core import AgentSession session = AgentSession() result = session.chat("你好,请介绍一下你自己") print(result.response_text)

第一次跑通时会看到控制台打印出完整的对话链路日志,包括模型返回的原始JSON、解析出的意图、最终答案。这里有个非常容易翻车的点:如果你用的模型服务返回格式不标准,比如reasoning模型把思考过程放在额外字段里,就需要在模型解析层做兼容,我在第5章会专门讲这个坑。

3.3 最小可运行配置:本地模型还是远程API

如果你没有远程模型的API key,这套源码也支持接本地模型服务。常见做法是用Ollama或vLLM起一个本地OpenAI兼容端点,然后直接把base_url指到本地端口。我一般会用Ollama拉一个小参数模型做开发调试,配置如下:

ollama pull qwen2.5:7b ollama serve # 默认监听 11434

然后把配置改成:

"base_url": "http://localhost:11434/v1", "model_name": "qwen2.5:7b"

本地模型的优势是调试成本为零,所有循环调用都在本机完成,方便打断点观察每一步的状态变化。但需要留意的是7B级别的模型在复杂工具调用场景下偶尔会返回格式错的JSON,这类问题在源码控制台日志里会以json.loads failed的形式暴露出来。我实测下来,如果本地模型给的候选工具超过8个,返回错误格式的概率会明显上升,建议开发阶段把工具数量控制在5个以内,生产环境再交给更强的大模型。

3.4 启动验证:三步确认Agent循环正常

跑通环境只是第一步,确认Agent循环逻辑正常才是关键。我建议按下面三步验证,每步都有明确的预期输出:

第一步验证单轮工具调用。让Agent执行一个明确需要调用工具的任务,比如查询当前时间或计算一串数字。预期输出是控制台出现Action: get_current_time和Observation: ...两条日志,说明模型正确识别了工具意图。

第二步验证多轮工具调用。给Agent一个需要连续使用两次工具才能完成的任务,比如「先查今天的日期,再算这个日期加上7天是几号」。预期输出是出现两次独立的Action-Observation循环。这里有个关键指标:两次Action之间模型不应重复调用相同工具,如果出现重复调用,说明上下文里缺少对已执行工具结果的记忆,要检查记忆层的注入逻辑。

第三步验证终止条件。在循环最多执行max_iterations次之后,Agent应该返回一个明确的最终答复,而不是继续空转。这套源码默认的上限是15次,如果你发现某些复杂任务15次不够用,可以把配置调高到30次,但要注意token消耗会成倍增加。

4. 核心模块实战拆解:工具注册、函数调用解析与记忆管理

4.1 工具注册机制:30行代码实现可扩展工具表

Agent系统里最核心的一段代码就是工具注册机制。这套源码采用装饰器+全局字典的方式,简洁且可扩展性强。看工具层的实现:

# agent/tools/registry.py from typing import Callable, Dict, Any import inspect import json TOOL_REGISTRY: Dict[str, Dict[str, Any]] = {} def register_tool(name: str, description: str, parameters: dict): """注册工具到全局表,参数schema遵循OpenAI function calling格式""" def decorator(func: Callable): TOOL_REGISTRY[name] = { "function": func, "description": description, "parameters": parameters, "metadata": { "source": func.__module__, "docstring": inspect.getdoc(func) } } return func return decorator def list_tools_for_model() -> list: """生成发送给模型的工具定义列表(只含描述与参数,不含函数本体)""" tools = [] for name, info in TOOL_REGISTRY.items(): tools.append({ "type": "function", "function": { "name": name, "description": info["description"], "parameters": info["parameters"] } }) return tools def invoke_tool(name: str, arguments: dict): """根据模型返回的工具名和参数执行实际函数,未注册工具时抛出可读异常""" if name not in TOOL_REGISTRY: raise KeyError(f"tool not registered: {name}, available: {list(TOOL_REGISTRY.keys())}") func = TOOL_REGISTRY[name]["function"] return func(**arguments)

这套设计的核心逻辑分三层:register_tool装饰器让新增工具只需要写一个普通函数加上两行装饰信息;list_tools_for_model负责把工具定义转换成模型能理解的JSON格式,这里必须用循环动态生成列表而不是手写常量,否则后续加工具就要改两处代码;invoke_tool是动态调用的关键,它把模型返回的字符串参数变成真实的Python函数调用。

参数schema的定义格式参考OpenAI的function calling规范,形如:

@register_tool( name="search_web", description="搜索互联网获取最新信息,适合查询新闻、事件、实时数据", parameters={ "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词,尽量精简"}, "max_results": {"type": "integer", "description": "返回结果条数,默认5"}, }, "required": ["query"] } ) def search_web(query: str, max_results: int = 5) -> str: # ... 实际搜索逻辑 return "搜索结果摘要"

参数schema里我最看重description字段的写法。很多人写工具描述时敷衍了事,比如「搜索工具」四个字完事,结果模型不知道该在什么场景下调用它。正确的写法是包含三个信息:工具能做什么、适合什么场景、有什么限制。模型虽然不会像人一样「理解」文字,但它对描述更具体的工具分配的概率权重明显更高,这个现象我在多个模型上反复验证过。

4.2 函数调用返回解析:处理模型输出中的脏JSON

工具调用链路里最脆弱的一环是解析模型的返回内容。大模型输出的JSON偶尔会夹杂额外文本,比如在JSON前面输出一段解释,或者把精简的JSON包在Markdown代码块里。直接json.loads会翻车,需要做一个容错解析层:

# agent/parser.py import json import re def extract_tool_call(raw_text: str) -> dict: """从模型原始输出中提取工具调用,兼容多种脏格式""" # 情况1:模型把JSON包在markdown代码块里 codeblock_match = re.search(r'```(?:json)?\s*({.*?})\s*```', raw_text, re.DOTALL) if codeblock_match: raw_text = codeblock_match.group(1) # 情况2:JSON前面有一段自然语言解释,取第一个{到最后一个} start = raw_text.find("{") end = raw_text.rfind("}") if start == -1 or end == -1 or end <= start: raise ValueError(f"no valid json object found in: {raw_text[:200]}") json_str = raw_text[start:end + 1] # 情况3:JSON内部有不规范的单引号或多余逗号 try: return json.loads(json_str) except json.JSONDecodeError: # 替换单引号为双引号(粗粒度修复,谨慎使用) cleaned = json_str.replace("'", '"') cleaned = re.sub(r',\s*}', '}', cleaned) try: return json.loads(cleaned) except json.JSONDecodeError as e: raise ValueError(f"failed to parse tool call after cleanup: {e}")

这里我要特别说明:replace("'", '"')这个粗暴修复只建议在开发阶段用,生产环境一定要把原始输出完整记录到日志里,否则等模型输出复杂嵌套结构时,单引号替换反而会制造更隐蔽的解析错误。正确做法是优先从模型层面约束返回格式,比如在system提示词里写死「只输出JSON,不要任何解释」,并且把response_format={"type": "json_object"}作为请求参数传入,绝大多数OpenAI兼容接口都支持这个参数。

解析层设计成独立模块还有一个隐藏好处——后续如果接入不同厂商的模型,它们的输出格式可能存在差异,你只需要改extract_tool_call这一个函数,编排层完全不用动。这就是模块边界的价值。

4.3 记忆管理:会话内上下文与持久化记忆

Agent系统如果没有记忆,多轮对话会非常蠢——模型每次都是无状态的,你上一轮告诉它的信息它全部忘光。这套源码实现了一个两层记忆结构:短期记忆是当轮会话的对话历史,每次请求都会完整发送给模型;长期记忆是跨会话的关键信息存储,按用户ID做隔离。

短期记忆的实现要点是裁剪策略。如果把所有历史消息都塞给模型,token消耗会随着对话轮数线性增长,最终达到上下文窗口上限。这套源码默认采用「滑动窗口+摘要压缩」策略:最近10条消息原样保留,更早的消息压缩成一段摘要文本。看代码:

# agent/memory.py from typing import List, Dict class SlidingWindowMemory: def __init__(self, recent_window: int = 10, summarize_threshold: int = 20): self.recent_window = recent_window self.summarize_threshold = summarize_threshold self.messages: List[Dict] = [] self.summary: str = "" def add_message(self, role: str, content: str): self.messages.append({"role": role, "content": content}) if len(self.messages) > self.summarize_threshold: self._compress() def _compress(self): """把最早的一半消息压缩进摘要,腾出空间""" to_summarize = self.messages[:self.summarize_threshold // 2] self.summary = self._call_llm_summary(to_summarize) self.messages = self.messages[self.summarize_threshold // 2:] def build_prompt(self) -> List[Dict]: """构造最终发送给模型的上下文,摘要放在最前面""" if self.summary: return [{"role": "system", "content": f"早前对话摘要: {self.summary}"}] + self.messages[-self.recent_window:] return self.messages[-self.recent_window:]

压缩时机和阈值的配比是个经验活。窗口太小模型记不住前面的事,窗口太大单轮token消耗会膨胀。实测下来,10条近期消息+20条总阈值的组合,在普通业务对话场景下能覆盖绝大多数连续操作场景,同时单轮token控制在合理范围。

4.4 编排循环:控制Agent的思考-行动-观察闭环

把前面的工具注册、结果解析、记忆管理串起来的核心是编排器。这里我摘一段核心的循环逻辑:

# agent/orchestrator.py def run_agent_task(self, task_description: str, max_iterations: int = 15) -> str: """执行一次完整的Agent任务循环""" self.memory.add_message("user", task_description) iteration = 0 while iteration < max_iterations: iteration += 1 messages = self.memory.build_prompt() messages.append({ "role": "system", "content": ( "你是一个Agent系统核心控制器。你应该通过思考决定下一步行动。" "如果任务已完成,请在回复末尾输出FINAL_ANSWER: 你的回答。" "如果信息不足,调用一个工具获取信息。" "必须严格按JSON格式输出,例如: " '{"thought": "我需要查询时间", "tool": "get_current_time", "arguments": {}}' ) }) response = self._call_model(messages, tools=list_tools_for_model()) raw_content = response["content"] if "FINAL_ANSWER" in raw_content: final_answer = raw_content.split("FINAL_ANSWER:")[-1].strip() self.memory.add_message("assistant", final_answer) return final_answer tool_call = extract_tool_call(raw_content) tool_name = tool_call.get("tool") tool_args = tool_call.get("arguments", {}) # 记录关键中间状态,方便排错 self.trace_log(f"[{iteration}] thought: {tool_call.get('thought')}") self.trace_log(f"[{iteration}] action: {tool_name}({tool_args})") try: observation = invoke_tool(tool_name, tool_args) except Exception as e: observation = f"tool execution error: {str(e)}" self.trace_log(f"[{iteration}] observation: {str(observation)[:200]}") self.memory.add_message("assistant", f"工具返回: {str(observation)}") return "任务未在限定步数内完成,请尝试拆分任务或调整提示词"

这段循环有几个细节值得注意。max_iterations是硬性终止条件,没有它模型可能陷入无限循环——比如它反复调用同一个搜索工具却无法从结果里找到答案。每次工具调用的observation被当作assistant消息记录进记忆,这样下次模型请求就能看到之前的调用结果,不会重复犯同一个错误。控制台trace日志是排错的生命线,生产环境建议直接接到日志采集系统。

5. 部署与避坑:从开发机到服务的五个翻车现场

5.1 用FastAPI封装成HTTP服务

Agent循环本身跑在Python进程里,要接入业务系统需要加一层HTTP接口。最常用的方案是FastAPI,代码量小且自带异步支持。这里给一个标准的服务封装思路:

# server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from agent.core import AgentSession import uvicorn app = FastAPI(title="Agent Service") sessions: dict[str, AgentSession] = {} class ChatRequest(BaseModel): session_id: str message: str use_fast_model: bool = False class ChatResponse(BaseModel): session_id: str response: str @app.post("/chat", response_model=ChatResponse) def chat(req: ChatRequest): if req.session_id not in sessions: sessions[req.session_id] = AgentSession() session = sessions[req.session_id] result = session.chat(req.message, use_fast_model=req.use_fast_model) return ChatResponse(session_id=req.session_id, response=result) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)

注意这里workers=1是刻意设置的。因为sessions字典存在进程内存里,多worker模式下多个进程各自维护一份session,用户请求如果被负载均衡到不同worker,上下文就断了。如果要支持多worker,必须把会话状态迁移到Redis这类外部存储。这个细节我在上线后才踩到,当时线上出现一种诡异现象:用户同一个session_id的对话,有时记得上下文有时不记得,查了半天才发现是worker间session隔离。

服务化部署推荐用Docker,Dockerfile的关键内容只有几行:

FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]

5.2 避坑记录:五个高频翻车现场

这五条是我实测和多人反馈中最常遇到的情况,按「现象→原因→解决」的方式列出来,都是血泪换来的。

坑一:模型频繁空转,不调用工具也不给最终答案。现象是控制台日志里模型输出一堆思考文本,但既没有tool字段也没有FINAL_ANSWER。原因多半是system提示词里对输出格式的约束不够强硬,模型在「自由发挥」。解决方法是把输出格式说明移到user消息里而不是system消息,实测效果显著,因为部分模型对system的遵循度弱于对user的遵循度。

坑二:工具调用参数频繁出错,比如search_web的max_results被传成字符串"5"而不是整数5。现象是工具函数抛出TypeError。原因是模型对参数类型的理解依赖schema的描述,但部分模型就是对数字类型不敏感。解决方法是工具函数内部加一层轻量类型转换,用int()强行转换数字字符串,而不是要求模型永远正确。

坑三:多轮对话之后token爆炸。现象是某次对话突然报context length exceeded。原因是记忆管理只压缩了历史对话,没压缩工具返回结果,而工具返回的内容往往又长又杂。解决方法是工具返回前先截断到指定长度(比如1000字符),并保持摘要结构,既省token又不丢关键信息。

坑四:本地模型跑Agent,工具定义超过8个时经常返回非法JSON。现象是extract_tool_call抛校验错误。原因是本地小参数模型对复杂function calling格式的处理能力有限,候选工具越多越容易混淆。解决方法是生产环境用API模型,本地模型只保留3-5个核心工具用于开发调试。

坑五:部署到服务器后首次请求超时,但本地测试一切正常。现象是nginx报504,服务日志显示模型调用阻塞。原因是模型服务配置的timeout=60是从连接开始计算的,服务器上首轮请求要经历DNS解析、TLS握手、模型排队等多个阶段,60秒不够。解决方法是把超时提高到180秒,同时给nginx配置对应的proxy_read_timeout。

5.3 生产环境部署前的配置检查清单

部署上线前建议按顺序过一遍清单,每项都有明确的检查方法。模型配置部分,确认base_url以/v1结尾,检查方法和模型名是否匹配,实测很多新用户把模型名填错导致反复报错。环境变量部分,API key不能硬编码进代码或Docker镜像,用环境变量注入,启动前检查变量是否存在,缺失时程序应该直接拒绝启动而不是默认一个假key,源码支持这种校验逻辑但需要显式开启。

会话管理部分,如果用了多worker,必须把session存储切到Redis,并设置合理的过期时间(比如30分钟无操作自动清理)。工具安全部分,涉及文件读写、网络请求等危险操作的工具,生产环境要加白名单限制,因为Agent一旦被注入恶意提示词,可能利用工具做越权操作。日志部分,输出完整trace链路,包括每次模型的原始响应和工具调用参数,线上排错如果没有trace日志等于瞎猜。

6. 再往下走一步:多Agent协作与可观测性增强

6.1 从单Agent到多Agent:主管-执行者模式改造

如果你已经把这套单Agent源码跑通,下一步改造方向大概率是多Agent协作——一个系统里同时跑多个Agent,各自负责不同领域。我推荐从主管-执行者模式入手,这套源码的模块结构对这类改造很友好。主管Agent不直接调用业务工具,它的职责是拆解用户任务、分发给下属执行者Agent、汇总结果并判断是否需要追问。

改造的关键点在于工具注册层的复用。让执行者Agent暴露成特殊形态的工具——主管的工具列表里增加一个「调用数据分析Agent」的条目,参数是任务描述;执行触发后,主管把子任务写入某个队列,执行者Agent处理完把结果返回给主管。这里最需要留意的是上下文隔离:主管的对话历史里不能混入执行者的内部工具调用记录,否则token浪费且容易产生逻辑混乱。常见做法是让执行者走独立的AgentSession实例,只把最终结果字符串传给主管,这样两个上下文的边界很清晰。

6.2 可观测性增强:把Agent执行链路变成可审计日志

Agent系统在生产环境里的调试难度远高于常规接口,它的一次任务可能包含十几次内部循环,每个环节都可能出错。这套源码自带的trace日志够用于开发调试,但生产环境建议换成结构化日志输出,方便接入日志平台做链路追踪。

我改造时用的最简单方案是把每次循环的关键信息拼接成一条JSON日志:

import logging import json logger = logging.getLogger("agent.trace") def trace_round(iteration, thought, action, args, observation): logger.info(json.dumps({ "event": "agent_round", "iteration": iteration, "thought": thought, "action": action, "args_preview": str(args)[:200], "observation_preview": str(observation)[:500], "token_cost": current_token_usage() }, ensure_ascii=False))

把trace_round调用埋进编排循环里,日志平台直接按event=agent_round过滤就能复现完整的执行链路。这里我有一个多年的习惯:每次上线的Agent系统必加一个「回放模式」(replay mode),把历史日志中的原始请求和模型响应喂给一个仿真器,不调真实工具,只看模型决策是否合理。这个模式找了几次线上事故的根因,比如某个工具改了入参格式,模型在日志回放里依然按旧格式调用导致报错——就是靠回放发现的。从那以后,我每次改工具层代码都会强制走一遍日志回放流程,确认没有引入回归问题再上线。希望这套路也能帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 23:14:43

企业AI落地全流程指南:场景评估、RAG与Agent实践

简介&#xff1a;科易网作为国家级企业服务平台&#xff0c;推出一份AI驱动创新赋能企业数智化转型的专题文档&#xff0c;面向受科技信息碎片化、技术资源匹配难、客户服务响应慢、人才培养周期长等困扰的企业管理者与科技创新服务从业者。文档系统梳理了AI技术图谱、AI技术情…

作者头像 李华
网站建设 2026/9/25 23:05:26

Apache Pulsar Functions 快速入门实战:从本地运行到集群部署

消息队列后端流处理 【免费下载链接】pulsar Apache Pulsar - distributed pub-sub messaging system 项目地址&#xff1a; https://gitcode.com/gh_mirrors/pulsar28/pulsar 点击查看 免费下载 本指南以 Apache Pulsar 的 Pulsar Functions 轻量级流处理模型为主题&#xff…

作者头像 李华
网站建设 2026/9/25 22:57:21

OpenClaw卸载残留清理指南:服务、配置、缓存三步彻底清除

卸载这类带后台服务的 AI 代理工具&#xff0c;最恼人的不是卸载本身&#xff0c;而是卸载完总觉得哪儿不对劲——端口还在监听&#xff0c;开机又弹出日志报错&#xff0c;翻遍系统目录还有一堆.json、.db、.log残留。OpenClaw 尤其典型&#xff0c;它既有 CLI 主程序&#xf…

作者头像 李华
网站建设 2026/9/25 22:53:54

杭州大平层全案整体设计服务商实力与用户口碑深度解析

什么是大平层全案整体设计大平层这类改善型住宅&#xff0c;拥有开阔的空间面积和优越的地段资源&#xff0c;已经成为众多改善型家庭的置业&#xff0c;而全案整体设计是适配大平层空间的专属家居服务模式&#xff0c;和传统家居服务有着本质区别。传统家居消费中&#xff0c;…

作者头像 李华
网站建设 2026/9/25 22:52:53

大宅设计公司避坑挑选指南:专业实力与用户口碑深度解析

大宅设计的底层逻辑&#xff1a;为什么你家的豪宅始终用不对空间说起大宅设计&#xff0c;很多人第一反应就是花钱买好看&#xff0c;但真正住过的业主都知道&#xff0c;一套能称之为家的大宅&#xff0c;从来不是效果图里的悬浮楼梯和网红软装堆砌出来的。从入户到起居&#…

作者头像 李华
网站建设 2026/9/25 22:51:42

基于Qt框架的幸存者游戏源码解析与改造实战

简介&#xff1a;这份基于Qt框架的幸存者游戏源码包&#xff0c;是南京大学高级程序设计课程的大作业&#xff0c;围绕C面向对象编程思想设计实现。项目包含基本地图与障碍物生成、玩家角色的移动/攻击/掉血/拾取、敌方单位移动策略与攻击逻辑、局内与全局双重强化系统、存档读…

作者头像 李华