云计算百科
云计算领域专业知识百科平台

第二章Agent 开发环境搭建与验证

我们的目标是让开发环境达到"能跑通一个最小 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,并初步理解"输入—推理—工具调用—返回—轨迹—验证"的基本链路。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 第二章Agent 开发环境搭建与验证
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!