我们的目标是让开发环境达到"能跑通一个最小 Agent"的状态:安装依赖、配置模型、封装工具、运行调用、保存轨迹、验证行为。同时,所有示例代码都尽量补成可直接拷贝运行的版本,并在需要特殊配置的地方给出运行方式说明。
一、环境搭建前的三个选择
在动手之前,需要先确定三件事:
| 开发语言 | 目前 Python 生态最成熟 | 初学者优先 Python |
| 模型接入 | 决定 Agent 的推理能力 | 先用一个稳定模型跑通 |
| 框架层级 | 决定后续扩展复杂度 | 先轻量,再逐步升级 |
框架层面,2026 年的选择已经比较清晰:
- LangChain / LangGraph:适合构建工具调用、RAG、多步工作流,社区生态成熟。
- CrewAI:适合快速搭建多角色 Agent 原型,但复杂状态管理较弱。
- LlamaIndex Workflows:适合知识库、文档检索、合规审查等数据密集型场景。
- Microsoft Agent Framework:适合 .NET / Azure / M365 生态。
- OpenAI Agents SDK / Claude Agent SDK:适合绑定单一模型栈、逻辑相对简单的 Agent。
- Dify / Coze:适合快速验证、低代码搭建,但定制能力和数据控制相对受限。
对我们来说,建议先用 Python + LangChain 跑通最小流程;如果要进入生产级复杂工作流,再考虑迁移到 LangGraph。
二、Python 基础环境配置
1. Python 版本
推荐安装 Python 3.10 ~ 3.12。
Python 3.10 以上版本对类型提示、异步支持和主流 Agent 框架兼容性更好。
安装时注意勾选:
1Add Python to PATH
安装完成后验证:
1python –version
2pip –version
2. 虚拟环境
Agent 项目通常会安装较多依赖,建议使用虚拟环境隔离,避免污染全局 Python。
1python -m venv .venv
2
3# Windows PowerShell
4.venv\\Scripts\\Activate.ps1
5
6# macOS / Linux
7source .venv/bin/activate
激活成功后,终端前面通常会出现 (.venv)。
如果 PowerShell 提示无法运行脚本,可以先执行:
1Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned
然后再激活环境。
3. 虚拟环境工具怎么选
| venv | Python 内置,轻量 | 新手、脚本、小型项目 |
| conda | 可管理 Python 版本和非 Python 依赖 | 数据科学、机器学习 |
| poetry | 依赖锁定、打包发布能力强 | 团队协作、开源项目 |
| uv | Rust 编写,安装和解析速度很快 | 追求效率、CI/CD、大型项目 |
| pipenv | 曾经流行,目前新项目不优先推荐 | 维护老项目 |
建议:
- 新手入门:venv + pip
- 数据科学 / 机器学习项目:conda
- 团队协作或需要发布包:poetry
- 追求安装速度和依赖锁定:uv
4. pip 镜像源
如果下载依赖较慢,可以临时使用国内镜像源,例如清华源:
1pip install langchain langchain-openai python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple
注意选择可信镜像源,避免安装来源不明的包。
三、开发工具建议
1. 代码编辑器 / IDE
| VS Code | 免费、轻量、插件生态丰富 | 通用首选 |
| PyCharm | Python 专业 IDE,调试能力强 | 大型项目、团队协作 |
| JupyterLab | 交互式编程,适合数据探索 | 数据分析、AI 实验 |
| Cursor | AI 深度集成,支持跨文件重构 | 追求效率的开发者 |
| Trae | 中文友好,基础版免费 | 国内开发者、快速原型 |
| Windsurf | 全局上下文感知强 | 复杂项目重构 |
| Qoder | 适配后端工程化 | 企业级后端开发 |
| CodeBuddy | 前端、小程序、H5 适配较好 | 前端与微信生态开发 |
推荐组合:
- 新手 / 通用开发:VS Code + Python + Pylance + 中文语言包。
- 大型 Python 项目:PyCharm 专业版。
- AI 辅助开发:Cursor、Trae 或 Windsurf。
- 数据探索:JupyterLab + VS Code。
VS Code 必装插件建议:
- Python
- Pylance
- Chinese (Simplified) Language Pack
- GitLens(可选)
- Jupyter(可选)
2. AI 编程辅助工具
AI 编程工具可以分为三类:
- AI 原生 IDE:Cursor、Windsurf、Trae、Qoder、CodeBuddy、Devin Desktop 等。
- AI 插件:GitHub Copilot、Tabnine、通义灵码、Kimi Code、Cline 等。
- 代码安全与审查工具:Sourcery、Snyk Code 等。
选型建议:
- 想要 稳定补全 + 多 IDE 支持:GitHub Copilot。
- 想要 跨文件理解 + Agent 模式:Cursor。
- 想要 中文友好 + 免费体验:Trae。
- 想要 企业级后端工程化:Qoder。
- 想要 数据隐私优先:Tabnine。
对 Agent 开发来说,AI 编程工具的价值主要在于:快速生成工具函数、补全 Pydantic 模型、解释报错信息、生成测试用例,以及辅助调试多步调用流程。
四、核心依赖安装
创建并激活虚拟环境后,安装基础依赖:
1pip install langchain langchain-openai langchain-community python-dotenv
如果需要本地模型,可以继续安装:
1pip install langchain-ollama
如果需要向量数据库:
1pip install chromadb
辅助库可按需安装:
1pip install pydantic requests rich tiktoken
验证环境:
1import sys
2import langchain
3
4print(f"Python: {sys.version}")
5print(f"LangChain: {langchain.__version__}")
如果正常输出版本号,说明基础环境已经就绪。
五、模型接入配置
Agent 的核心推理能力来自大模型。配置时不要把 API Key 写进代码,推荐使用 .env 文件管理。
创建 .env:
1OPENAI_API_KEY=your_api_key_here
2MODEL_NAME=gpt-4o
3MODEL_BASE_URL=
如果使用的是兼容 OpenAI 接口的模型服务,可以填写:
1OPENAI_API_KEY=your_api_key_here
2MODEL_NAME=qwen-plus
3MODEL_BASE_URL=https://your-api-endpoint/v1
如果 MODEL_BASE_URL 为空,代码会自动使用 OpenAI 默认地址。
在代码中加载:
1from dotenv import load_dotenv
2import os
3
4load_dotenv()
5
6api_key = os.getenv("OPENAI_API_KEY")
7model_name = os.getenv("MODEL_NAME", "gpt-4o")
8base_url = os.getenv("MODEL_BASE_URL", "").strip()
初始化模型:
1from langchain_openai import ChatOpenAI
2
3llm_kwargs = {
4 "model": model_name,
5 "temperature": 0.2,
6 "api_key": api_key,
7}
8
9if base_url:
10 llm_kwargs["base_url"] = base_url
11
12llm = ChatOpenAI(**llm_kwargs)
工具调用场景建议把 temperature 设低一些,例如 0 到 0.2,减少随机输出。
六、最小 Agent 结构
一个最小 Agent 至少需要四部分:
1用户输入
2 -> Prompt 定义角色与目标
3 -> LLM 推理
4 -> 工具调用
5 -> 工具返回结果
6 -> LLM 生成最终回答
先定义一个简单工具,例如天气查询工具:
1from langchain_core.tools import tool
2
3@tool
4def get_weather(city: str) -> str:
5 """查询指定城市的天气信息。
6
7 参数:
8 – city: 城市名称,例如"郑州""北京""上海"
9 """
10 if not city:
11 return "缺少城市名称,无法查询天气。"
12
13 weather_data = {
14 "郑州": "晴,22°C,微风",
15 "北京": "多云,18°C,北风2级",
16 "上海": "小雨,20°C,东南风3级",
17 }
18
19 return weather_data.get(city, f"未找到 {city} 的天气数据,请确认城市名称。")
然后创建 Agent:
1from langchain.agents import create_tool_calling_agent, AgentExecutor
2from langchain_core.prompts import ChatPromptTemplate
3
4tools = [get_weather]
5
6prompt = ChatPromptTemplate.from_messages([
7 ("system", "你是一个智能助手,可以根据用户需求调用工具。"),
8 ("human", "{input}"),
9 ("placeholder", "{agent_scratchpad}"),
10])
11
12agent = create_tool_calling_agent(llm, tools, prompt)
13
14agent_executor = AgentExecutor(
15 agent=agent,
16 tools=tools,
17 verbose=True,
18 max_iterations=5,
19 handle_parsing_errors=True
20)
运行测试:
1result = agent_executor.invoke({"input": "郑州今天天气怎么样?"})
2print(result)
verbose=True 可以打印 Agent 的思考过程、工具调用和返回结果,非常适合调试。
七、完整可运行脚本:agent_demo.py
下面把前面的片段整合成一个完整脚本,可以直接拷贝运行。
1"""
2Agent 开发环境最小验证脚本
3
4运行方式:
51. 创建虚拟环境:python -m venv .venv
62. 激活环境:
7 – Windows PowerShell: .venv\\Scripts\\Activate.ps1
8 – macOS / Linux: source .venv/bin/activate
93. 安装依赖:pip install langchain langchain-openai python-dotenv
104. 在项目根目录创建 .env 文件,写入 OPENAI_API_KEY
115. 运行:python agent_demo.py
12"""
13
14import os
15import json
16import time
17from datetime import datetime
18from dotenv import load_dotenv
19from langchain_openai import ChatOpenAI
20from langchain_core.tools import tool
21from langchain.agents import create_tool_calling_agent, AgentExecutor
22from langchain_core.prompts import ChatPromptTemplate
23
24# —————————-
25# 1. 加载环境变量
26# —————————-
27load_dotenv()
28
29OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
30MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o")
31MODEL_BASE_URL = os.getenv("MODEL_BASE_URL", "").strip()
32
33if not OPENAI_API_KEY:
34 raise RuntimeError(
35 "未找到 OPENAI_API_KEY,请在项目根目录创建 .env 文件并写入 API Key。"
36 )
37
38# —————————-
39# 2. 初始化模型
40# —————————-
41llm_kwargs = {
42 "model": MODEL_NAME,
43 "temperature": 0.2,
44 "api_key": OPENAI_API_KEY,
45}
46
47if MODEL_BASE_URL:
48 llm_kwargs["base_url"] = MODEL_BASE_URL
49
50llm = ChatOpenAI(**llm_kwargs)
51
52# —————————-
53# 3. 定义工具
54# —————————-
55
56@tool
57def get_weather(city: str) -> str:
58 """查询指定城市的天气信息。
59
60 参数:
61 – city: 城市名称,例如"郑州""北京""上海"
62 """
63 if not city:
64 return "缺少城市名称,无法查询天气。"
65
66 weather_data = {
67 "郑州": "晴,22°C,微风",
68 "北京": "多云,18°C,北风2级",
69 "上海": "小雨,20°C,东南风3级",
70 }
71
72 return weather_data.get(city, f"未找到 {city} 的天气数据,请确认城市名称。")
73
74
75@tool
76def get_current_time(dummy: str = "") -> str:
77 """获取当前日期和时间。
78
79 参数:
80 – dummy: 可选占位参数,不需要传入实际内容
81 """
82 now = datetime.now()
83 return now.strftime("%Y年%m月%d日 %H:%M:%S")
84
85
86tools = [get_weather, get_current_time]
87
88# —————————-
89# 4. 创建 Agent
90# —————————-
91
92prompt = ChatPromptTemplate.from_messages(
93 [
94 ("system", "你是一个智能助手,可以根据用户需求调用工具。"),
95 ("human", "{input}"),
96 ("placeholder", "{agent_scratchpad}"),
97 ]
98)
99
100agent = create_tool_calling_agent(llm, tools, prompt)
101
102agent_executor = AgentExecutor(
103 agent=agent,
104 tools=tools,
105 verbose=True,
106 max_iterations=5,
107 handle_parsing_errors=True,
108)
109
110# —————————-
111# 5. 运行测试
112# —————————-
113
114test_cases = [
115 {"input": "郑州今天天气怎么样?"},
116 {"input": "现在几点了?"},
117 {"input": "北京和上海今天的天气分别如何?"},
118]
119
120traces = []
121
122for case in test_cases:
123 print("\\n" + "=" * 60)
124 print(f"测试输入:{case['input']}")
125 print("=" * 60)
126
127 start_time = time.time()
128 result = agent_executor.invoke(case)
129 elapsed = round(time.time() – start_time, 2)
130
131 trace = {
132 "input": case["input"],
133 "output": result.get("output", ""),
134 "elapsed_seconds": elapsed,
135 "timestamp": datetime.now().isoformat(),
136 }
137
138 traces.append(trace)
139
140 print("\\n最终回答:")
141 print(result.get("output", ""))
142 print(f"\\n耗时:{elapsed} 秒")
143
144# —————————-
145# 6. 保存轨迹
146# —————————-
147
148output_dir = "agent_traces"
149os.makedirs(output_dir, exist_ok=True)
150
151output_file = os.path.join(output_dir, "traces.json")
152
153with open(output_file, "w", encoding="utf-8") as f:
154 json.dump(traces, f, ensure_ascii=False, indent=2)
155
156print(f"\\n轨迹已保存到:{output_file}")
运行方式:
1python agent_demo.py
八、轨迹检查脚本:check_traces.py
这个脚本用于验证 Agent 是否真的产生了输出,以及输出是否包含预期信息。
1"""
2Agent 轨迹检查脚本
3
4运行方式:
51. 先运行:python agent_demo.py
62. 再运行:python check_traces.py
7"""
8
9import json
10import os
11
12TRACE_FILE = os.path.join("agent_traces", "traces.json")
13
14if not os.path.exists(TRACE_FILE):
15 raise FileNotFoundError(
16 f"未找到轨迹文件 {TRACE_FILE},请先运行 agent_demo.py。"
17 )
18
19with open(TRACE_FILE, "r", encoding="utf-8") as f:
20 traces = json.load(f)
21
22print(f"共加载 {len(traces)} 条轨迹\\n")
23
24for idx, trace in enumerate(traces, start=1):
25 input_text = trace.get("input", "")
26 output_text = trace.get("output", "")
27 elapsed = trace.get("elapsed_seconds", 0)
28
29 has_output = bool(output_text and output_text.strip())
30 mentions_weather = "天气" in output_text
31 mentions_time = "时间" in output_text or "日期" in output_text
32 mentions_uncertainty = any(
33 word in output_text
34 for word in ["无法确认", "未找到", "请确认", "数据为空"]
35 )
36
37 print(f"轨迹 {idx}")
38 print(f"输入:{input_text}")
39 print(f"输出:{output_text}")
40 print(f"耗时:{elapsed} 秒")
41 print(f"有输出:{has_output}")
42 print(f"包含天气信息:{mentions_weather}")
43 print(f"包含时间信息:{mentions_time}")
44 print(f"包含不确定性提示:{mentions_uncertainty}")
45 print("-" * 60)
运行方式:
1python check_traces.py
注意:当前版本只做文本层面的基础检查。如果需要更严格地验证"是否真正调用了某个工具",可以在后续版本中接入 LangChain 的回调机制,记录完整的工具调用事件。
九、工具封装规范
工具是 Agent 与现实世界交互的入口。封装工具时要注意几点:
名称清晰
工具名要能表达用途,例如 get_weather、search_orders、send_email。
描述完整
docstring 或描述字段要写清楚工具用途、参数含义和返回格式。模型会依赖这些信息判断是否调用工具。
参数结构化
尽量使用明确类型,例如 str、int、float、list[str],避免让模型猜测参数格式。
错误可解释
工具失败时应返回可读错误信息,而不是直接抛出异常,否则 Agent 可能无法继续规划。
权限可控
删除、修改、发送类工具需要额外增加权限校验,不能直接暴露给所有用户。
示例:
1@tool
2def search_orders(user_id: str, order_id: str = "") -> str:
3 """查询用户订单信息。
4
5 参数:
6 – user_id: 用户ID
7 – order_id: 可选,订单ID
8 """
9 if not user_id:
10 return "缺少用户ID,无法查询订单。"
11 return f"用户 {user_id} 的订单查询成功"
十、记忆与上下文管理
默认情况下,Agent 每次调用都是"无状态"的。要实现多轮对话,需要显式配置记忆。
最简单的方式是使用对话历史:
1from langchain.memory import ConversationBufferMemory
2
3memory = ConversationBufferMemory(
4 memory_key="chat_history",
5 return_messages=True
6)
如果对话很长,可以使用摘要记忆,避免上下文无限增长:
1from langchain.memory import ConversationSummaryMemory
2
3memory = ConversationSummaryMemory(
4 llm=llm,
5 memory_key="chat_history",
6 return_messages=True
7)
企业级场景中,还可以把历史对话存入数据库或向量库,实现长期记忆。
需要注意:记忆不是越多越好。过长的上下文会增加成本,也可能引入噪声。生产环境通常需要设置最大轮数、摘要压缩或会话过期策略。
十一、Agent 验证方法
Agent 验证和普通接口测试不同。普通接口测试主要看"返回结果是否正确",但 Agent 还需要看"过程是否可靠"。
一个看似正确的回答,可能来自以下问题:
- 工具根本没有被调用;
- 工具参数传错了;
- 工具返回为空,但模型编造了数据;
- Agent 跳过了必要的校验步骤;
- 多轮对话后记忆出现偏差。
所以我们验证 Agent 时,至少要看三层:
| 工具层 | 是否调用正确工具、参数是否正确 | 参数错误、工具未调用 |
| 轨迹层 | 是否基于真实返回继续推理 | 忽略失败、跳步、编造 |
| 结果层 | 最终回答是否满足任务 | 遗漏条件、表达不准确 |
十二、最小验证流程
我们可以采用一个简化版的六步验证流程:
Plan:定义任务目标
明确 Agent 必须完成哪些动作。例如:必须调用天气工具、必须引用真实数据、工具为空时必须提示无法确认。
Data:准备测试数据
包括正常返回、空返回、错误返回、边界数据。
Trace:保存执行轨迹
记录工具调用名称、参数、返回结果、模型思考过程。
Run:多次运行
Agent 具有非确定性,同一个输入可能产生不同路径。关键场景至少运行多次。
Eval:自动断言 + 人工评审
可用代码检查工具名、参数、字段是否存在;对开放式回答再用 LLM 做质量评审。
Report:定位问题
报告不能只给分数,要指出哪条用例失败、哪次工具调用异常、哪个参数错误。
十三、一个简单轨迹检查示例
这里把最小轨迹检查函数补全:
1def evaluate_trace(case, trace):
2 tool_calls = trace.get("tool_calls", [])
3 final_text = trace.get("output", "")
4 tool_results = trace.get("tool_results", [])
5
6 expected_tool = case["expected_tool"]
7 expected_args = case.get("expected_arguments", {})
8
9 actual = next((c for c in tool_calls if c["name"] == expected_tool), None)
10
11 tool_called = actual is not None
12 params_ok = tool_called and all(
13 actual["arguments"].get(k) == v for k, v in expected_args.items()
14 )
15
16 has_data = any(r for r in tool_results if r and str(r).strip())
17 mentions_uncertainty = any(
18 word in final_text
19 for word in ["无法确认", "未找到", "请确认", "数据为空"]
20 )
21
22 if not tool_called:
23 return "FAIL: 未调用预期工具"
24
25 if not params_ok:
26 return "FAIL: 工具参数不正确"
27
28 if not has_data and not mentions_uncertainty:
29 return "FAIL: 工具无数据但模型未提示不确定性"
30
31 return "PASS"
这个函数适合作为“轨迹检查”的入门示例。它不依赖特定框架,我们可以先把 Agent 运行结果保存成统一格式,再用这个函数做基础断言。
十四、测试用例示例
我们可以准备一组最小测试用例,用来覆盖正常路径和异常路径:
1test_cases = [
2 {
3 "name": "正常天气查询",
4 "input": "郑州今天天气怎么样?",
5 "expected_tool": "get_weather",
6 "expected_arguments": {"city": "郑州"},
7 },
8 {
9 "name": "未知城市天气",
10 "input": "火星今天天气如何?",
11 "expected_tool": "get_weather",
12 "expected_arguments": {"city": "火星"},
13 },
14 {
15 "name": "时间查询",
16 "input": "现在几点了?",
17 "expected_tool": "get_current_time",
18 "expected_arguments": {},
19 },
20]
运行检查时可以这样组织:
1results = []
2
3for case in test_cases:
4 trace = run_agent(case["input"])
5 status = evaluate_trace(case, trace)
6 results.append({"case": case["name"], "status": status})
7
8for r in results:
9 print(r["case"], "->", r["status"])
这里的 run_agent 需要根据实际使用的框架替换成真实调用。针对本章 agent_demo.py 中的 LangChain Agent,我们可以这样写:
1def run_agent(user_input: str) -> dict:
2 """执行一次 Agent 调用,并返回结构化轨迹。"""
3
4 result = agent_executor.invoke({"input": user_input})
5
6 intermediate_steps = result.get("intermediate_steps", [])
7
8 tool_calls = []
9 tool_results = []
10
11 for step in intermediate_steps:
12 if isinstance(step, (list, tuple)) and len(step) >= 2:
13 action = step[0]
14 output = step[1]
15
16 tool_calls.append({
17 "name": getattr(action, "tool", None),
18 "arguments": getattr(action, "tool_input", None),
19 })
20 tool_results.append(str(output))
21
22 return {
23 "input": user_input,
24 "output": result.get("output", ""),
25 "tool_calls": tool_calls,
26 "tool_results": tool_results,
27 "intermediate_steps": intermediate_steps,
28 }
需要注意的是,intermediate_steps 的具体结构会随 LangChain 版本变化。如果后续发现 step[0] 不是预期的 action 对象,我们可以把 run_agent 改成只依赖 result.get("output") 做基础验证,再逐步接入回调机制来记录完整工具调用事件。
十五、完整验证脚本:verify_agent.py
把 run_agent、evaluate_trace 和测试用例整合到一个可直接运行的脚本中:
1"""
2Agent 轨迹验证脚本
3
4运行方式:
51. 先运行:python agent_demo.py
62. 再运行:python verify_agent.py
7"""
8
9import os
10import json
11from dotenv import load_dotenv
12from langchain_openai import ChatOpenAI
13from langchain_core.tools import tool
14from langchain.agents import create_tool_calling_agent, AgentExecutor
15from langchain_core.prompts import ChatPromptTemplate
16
17# —————————-
18# 1. 加载环境变量
19# —————————-
20load_dotenv()
21
22OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
23MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o")
24MODEL_BASE_URL = os.getenv("MODEL_BASE_URL", "").strip()
25
26if not OPENAI_API_KEY:
27 raise RuntimeError(
28 "未找到 OPENAI_API_KEY,请在项目根目录创建 .env 文件并写入 API Key。"
29 )
30
31# —————————-
32# 2. 初始化模型
33# —————————-
34llm_kwargs = {
35 "model": MODEL_NAME,
36 "temperature": 0.2,
37 "api_key": OPENAI_API_KEY,
38}
39
40if MODEL_BASE_URL:
41 llm_kwargs["base_url"] = MODEL_BASE_URL
42
43llm = ChatOpenAI(**llm_kwargs)
44
45# —————————-
46# 3. 定义工具
47# —————————-
48
49@tool
50def get_weather(city: str) -> str:
51 """查询指定城市的天气信息。
52
53 参数:
54 – city: 城市名称,例如"郑州""北京""上海"
55 """
56 if not city:
57 return "缺少城市名称,无法查询天气。"
58
59 weather_data = {
60 "郑州": "晴,22°C,微风",
61 "北京": "多云,18°C,北风2级",
62 "上海": "小雨,20°C,东南风3级",
63 }
64
65 return weather_data.get(city, f"未找到 {city} 的天气数据,请确认城市名称。")
66
67
68@tool
69def get_current_time(dummy: str = "") -> str:
70 """获取当前日期和时间。
71
72 参数:
73 – dummy: 可选占位参数,不需要传入实际内容
74 """
75 from datetime import datetime
76 now = datetime.now()
77 return now.strftime("%Y年%m月%d日 %H:%M:%S")
78
79
80tools = [get_weather, get_current_time]
81
82# —————————-
83# 4. 创建 Agent
84# —————————-
85
86prompt = ChatPromptTemplate.from_messages(
87 [
88 ("system", "你是一个智能助手,可以根据用户需求调用工具。"),
89 ("human", "{input}"),
90 ("placeholder", "{agent_scratchpad}"),
91 ]
92)
93
94agent = create_tool_calling_agent(llm, tools, prompt)
95
96agent_executor = AgentExecutor(
97 agent=agent,
98 tools=tools,
99 verbose=False,
100 max_iterations=5,
101 handle_parsing_errors=True,
102)
103
104# —————————-
105# 5. 封装 run_agent
106# —————————-
107
108def run_agent(user_input: str) -> dict:
109 """执行一次 Agent 调用,并返回结构化轨迹。"""
110
111 result = agent_executor.invoke({"input": user_input})
112
113 intermediate_steps = result.get("intermediate_steps", [])
114
115 tool_calls = []
116 tool_results = []
117
118 for step in intermediate_steps:
119 if isinstance(step, (list, tuple)) and len(step) >= 2:
120 action = step[0]
121 output = step[1]
122
123 tool_calls.append({
124 "name": getattr(action, "tool", None),
125 "arguments": getattr(action, "tool_input", None),
126 })
127 tool_results.append(str(output))
128
129 return {
130 "input": user_input,
131 "output": result.get("output", ""),
132 "tool_calls": tool_calls,
133 "tool_results": tool_results,
134 "intermediate_steps": intermediate_steps,
135 }
136
137# —————————-
138# 6. 封装 evaluate_trace
139# —————————-
140
141def evaluate_trace(case, trace):
142 """对一次 Agent 调用轨迹做基础断言。"""
143
144 tool_calls = trace.get("tool_calls", [])
145 final_text = trace.get("output", "")
146 tool_results = trace.get("tool_results", [])
147
148 expected_tool = case["expected_tool"]
149 expected_args = case.get("expected_arguments", {})
150
151 actual = next((c for c in tool_calls if c["name"] == expected_tool), None)
152
153 tool_called = actual is not None
154 params_ok = tool_called and all(
155 actual["arguments"].get(k) == v for k, v in expected_args.items()
156 )
157
158 has_data = any(r for r in tool_results if r and str(r).strip())
159 mentions_uncertainty = any(
160 word in final_text
161 for word in ["无法确认", "未找到", "请确认", "数据为空"]
162 )
163
164 if not tool_called:
165 return "FAIL: 未调用预期工具"
166
167 if not params_ok:
168 return "FAIL: 工具参数不正确"
169
170 if not has_data and not mentions_uncertainty:
171 return "FAIL: 工具无数据但模型未提示不确定性"
172
173 return "PASS"
174
175# —————————-
176# 7. 测试用例
177# —————————-
178
179test_cases = [
180 {
181 "name": "正常天气查询",
182 "input": "郑州今天天气怎么样?",
183 "expected_tool": "get_weather",
184 "expected_arguments": {"city": "郑州"},
185 },
186 {
187 "name": "未知城市天气",
188 "input": "火星今天天气如何?",
189 "expected_tool": "get_weather",
190 "expected_arguments": {"city": "火星"},
191 },
192 {
193 "name": "时间查询",
194 "input": "现在几点了?",
195 "expected_tool": "get_current_time",
196 "expected_arguments": {},
197 },
198]
199
200# —————————-
201# 8. 运行验证
202# —————————-
203
204print("开始 Agent 轨迹验证…\\n")
205
206results = []
207
208for case in test_cases:
209 print(f"运行用例:{case['name']}")
210 print(f" 输入:{case['input']}")
211
212 trace = run_agent(case["input"])
213 status = evaluate_trace(case, trace)
214
215 results.append({"case": case["name"], "status": status})
216
217 print(f" 状态:{status}")
218 print(f" 输出:{trace['output'][:100]}…")
219 print()
220
221# —————————-
222# 9. 汇总结果
223# —————————-
224
225print("=" * 60)
226print("验证结果汇总:")
227print("=" * 60)
228
229for r in results:
230 print(f"{r['case']} -> {r['status']}")
231
232pass_count = sum(1 for r in results if r["status"] == "PASS")
233fail_count = sum(1 for r in results if r["status"] != "PASS")
234
235print(f"\\n总计:{len(results)} 条用例,{pass_count} 通过,{fail_count} 失败")
236
237# —————————-
238# 10. 保存验证报告
239# —————————-
240
241output_dir = "agent_traces"
242os.makedirs(output_dir, exist_ok=True)
243
244report_file = os.path.join(output_dir, "verify_report.json")
245
246with open(report_file, "w", encoding="utf-8") as f:
247 json.dump(results, f, ensure_ascii=False, indent=2)
248
249print(f"\\n验证报告已保存到:{report_file}")
运行方式:
1python verify_agent.py
十六、常见运行问题排查
| OPENAI_API_KEY 缺失 | 未创建 .env 或文件路径错误 | 确认 .env 与脚本同目录 |
| 模型调用失败 | API Key 无效或余额不足 | 检查 Key、额度、服务状态 |
| 工具未被调用 | 工具描述不清晰 | 完善 docstring 和参数说明 |
| 返回空结果 | 工具返回为空且模型未兜底 | 增加不确定性提示逻辑 |
| 依赖安装失败 | 网络或镜像源问题 | 使用可信国内镜像源 |
| 虚拟环境未激活 | 依赖装到全局环境 | 重新激活 .venv 后运行 |
| 中文输出乱码 | 终端编码不一致 | 使用 UTF-8 终端或 IDE 运行 |
| 多轮对话丢失上下文 | 未配置记忆 | 启用对话历史或摘要记忆 |
十七、本章可直接交付的脚本清单
本章完成后,我们应拥有以下文件:
1project/
2├── .env
3├── agent_demo.py
4├── verify_agent.py
5├── check_traces.py
6└── agent_traces/
7 ├── traces.json
8 └── verify_report.json
运行顺序为:
1python agent_demo.py
2python verify_agent.py
3python check_traces.py
如果后续要扩展,我们可以把 agent_demo.py 拆成:
1project/
2├── .env
3├── main.py
4├── tools/
5│ └── weather.py
6├── agent/
7│ └── executor.py
8├── traces/
9│ └── traces.json
10└── tests/
11 └── test_traces.py
但教程初期不建议拆得太散。我们先用一个文件跑通完整流程,更容易建立信心。
十八、本章小结
这一章我们完成了 Agent 开发环境的闭环:
- 选择了 Python + LangChain 作为入门栈;
- 配置了虚拟环境、依赖和模型接入;
- 封装了可运行的天气查询和时间查询工具;
- 创建了一个最小 Agent,并支持多轮测试;
- 保存了运行轨迹,便于后续调试;
- 提供了 run_agent 封装、evaluate_trace 断言和测试用例;
- 提供了轨迹检查脚本、验证脚本和常见问题排查表。
到这里,我们已经可以独立运行一个最小 Agent,并初步理解"输入—推理—工具调用—返回—轨迹—验证"的基本链路。
网硕互联帮助中心





评论前必须登录!
注册