1. 项目概览
项目地址:https://github.com/bojieli/ai-agent-book
web-search-agent 是《深入理解 AI Agent》第 1 章实验 1-2 的配套项目,展示如何基于 Kimi K3 与 Moonshot 官方 Formula 工具实现一个自主网络搜索 Agent。它不是简单的"模型 + 搜索 API 拼接",而是完整实现了 ReAct 循环:模型先思考是否需要搜索,再通过官方 web_search 工具发起检索,读取 Fiber 结果后继续多轮推理,直到证据充分并生成最终答案。
从工程角度看,该仓库重点演示了以下能力:
- 使用 Kimi K3 的官方工具调用协议,而不是自定义伪工具。
- 通过 Formula 路由动态获取标准
web_searchfunction declaration。 - 将模型返回的工具名与原始参数不做篡改地转发给 Formula Fibers。
- 仅接受 HTTP 成功且
status == "succeeded"的 Fiber 结果。 - 支持多轮搜索、对话历史、trace 记录、离线演示、CLI 交互与实验验证。
- 提供 pytest 测试套件,覆盖 Agent 循环、配置解析、CLI、示例与实验验收逻辑。
1.1 学习价值
这个项目适合作为 AI Agent 工程入门样本,原因在于它同时覆盖了 Agent 应用的几个核心层面:
| 学习层面 | 项目中的体现 |
|---|---|
| Agent 基础范式 | ReAct:think → act → observe → answer |
| 工具调用 | 动态拉取 Moonshot 官方 web_search 工具声明 |
| 多轮执行 | search_and_answer(..., max_iterations) 控制搜索轮数 |
| 可观测性 | trace 记录每一步模型思考、工具调用、观察结果与最终回答 |
| 工程封装 | agent.py、config.py、main.py、examples.py 职责分离 |
| 测试保障 | 单元测试覆盖工具流、异常 JSON、迭代上限、配置映射等 |
| 实验复现 | run_experiment_1_2.py 生成带校验信息的实验证据 |
对于新手,它能帮助理解"为什么 Agent 不是一次 ChatCompletion 调用";对于有经验的开发者,它提供了一个可扩展的官方工具接入模板。
1.2 解决的行业痛点
很多搜索型 Agent demo 存在以下问题:
- 工具 schema 手写,容易与平台官方定义不一致。
- 搜索参数被中间层擅自修改,导致行为不可复现。
- 只演示单轮搜索,无法处理复杂问题。
- 缺少失败处理、trace 记录和测试。
- 在线结果不可验证,实验结论难以追溯。
本项目通过官方 Formula 路由、原始参数转发、严格 Fiber 成功判定、trace 与 evidence 机制,缓解了这些问题。它强调"模型作为 Agent"的最小可信实现:工具定义来自官方,执行路径可追踪,实验结果可校验。
1.3 前置知识要求
阅读和运行本项目前,建议具备以下基础:
- Python 基础:函数、类、类型注解、虚拟环境。
- HTTP/JSON 基础:理解 API 请求、响应状态码、JSON 序列化。
- LLM API 基础:了解 chat completions、messages、tool calls。
- Agent 基础概念:了解 ReAct、工具调用、多轮推理。
- 命令行基础:会执行
pip install、python script.py、pytest。 - 可选:了解
pytest、unittest.mock、dotenv、argparse。
如果不了解 Kimi Formula 也没关系,项目 README 已明确说明其核心流程;但需要注意,本仓库不实现本地搜索引擎,搜索能力由 Moonshot 托管。
2. 目录结构速览
仓库目录较小,适合从入口文件、核心 Agent、测试与验证数据四个方向阅读。
text
.
├── README.md
├── agent.py
├── config.py
├── env.example
├── examples.py
├── main.py
├── pytest.ini
├── quickstart.py
├── requirements.txt
├── run_experiment_1_2.py
├── tests/
│ ├── conftest.py
│ ├── test_agent.py
│ ├── test_config.py
│ ├── test_examples.py
│ ├── test_experiment_1_2.py
│ ├── test_main.py
│ └── test_offline_demo.py
└── validation/
├── latest.json
├── real_20260729T154442Z/
│ ├── evidence.json
│ └── evidence.sha256
├── real_20260729T155735Z/
│ ├── evidence.json
│ └── evidence.sha256
├── real_20260729T163003Z/
│ ├── evidence.json
│ └── evidence.sha256
└── real_20260729T163623Z/
├── evidence.json
└── evidence.sha256
目录职责如下:
| 路径 | 职责 |
|---|---|
README.md |
项目说明、架构图、Formula 路由说明、快速开始 |
agent.py |
核心 Web Search Agent、工具执行、离线 demo、CLI 入口 |
config.py |
Kimi/OpenRouter 等配置读取与校验 |
main.py |
主 CLI,支持单问、交互模式、JSON 输出 |
quickstart.py |
面向新手的一键体验脚本 |
examples.py |
高级用法:批量搜索、上下文搜索、对比搜索、事实核查 |
run_experiment_1_2.py |
实验 1-2 执行与 evidence 校验 |
tests/ |
pytest 测试套件 |
validation/ |
已生成的实验证据与 SHA256 校验文件 |
requirements.txt |
单项目兼容安装依赖 |
env.example |
环境变量示例文件 |
pytest.ini |
pytest 配置 |
阅读建议顺序:
- 先读
README.md的 Architecture 与 Exact Formula route。 - 再读
agent.py中WebSearchAgent.search_and_answer。 - 接着看
_get_tools、_execute_formula、_chat如何串起工具调用。 - 然后看
main.py了解 CLI 参数与输出。 - 最后阅读
tests/和run_experiment_1_2.py,理解验收标准。
3. 核心模块与职责
3.1 agent.py:核心 Agent 实现
agent.py 是仓库最重要的模块,类为 WebSearchAgent。它负责:
- 初始化 API Key、base URL、模型名与 verbose 模式。
- 获取官方 Formula 工具声明。
- 调用 Kimi chat completions。
- 执行模型请求的
web_search工具。 - 将 Fiber 输出作为 observation 继续送回模型。
- 控制最大迭代次数。
- 记录 conversation history 与 trace。
- 提供离线确定性 demo。
核心类方法清单:
| 方法 | 作用 |
|---|---|
__init__(api_key, base_url, model, verbose) |
初始化 Agent 配置 |
_emit(step) |
输出或记录 trace step |
_get_tools() |
获取 Formula 工具声明 |
_execute_formula(name, raw_arguments) |
调用 Formula Fiber 执行工具 |
_get_system_prompt() |
返回系统提示词 |
_chat(messages) |
调用 chat completions |
search_and_answer(user_question, max_iterations) |
执行 ReAct 主循环 |
clear_history() |
清空对话历史 |
get_conversation_history() |
获取对话历史 |
get_trace() |
获取执行 trace |
模块级函数还包括:
_reasoning_safe_temperature(model, requested):为推理模型调整到支持的 temperature。format_trace_step(step, max_len):格式化 trace,避免内容过长。search_impl(arguments):搜索实现相关函数。is_failure_answer(answer):判断答案是否为失败兜底。run_offline_demo(question, verbose):运行离线 ReAct 演示。main():agent.py的命令行入口。
3.2 config.py:配置与 Provider 选择
config.py 定义 Config 类,负责从环境变量和 .env 中读取配置。摘要显示其包含:
Config.validate(cls)Config.get_api_key(cls, api_key)
测试文件进一步表明该模块还涉及:
- 模型到 OpenRouter 的映射。
- 未知模型使用配置的 OpenRouter 默认模型。
- 未启用替换时保留未知模型。
- 主 Provider key 存在时保留主 Provider。
- 主 key 缺失时使用 OpenRouter。
- GPT-5 在两个 key 都存在时优先 OpenRouter。
- Provider 解析必须存在 key。
具体环境变量名称未在解析摘要中完整列出,需以 env.example 或源码为准。
3.3 main.py:命令行主程序
main.py 提供更正式的 CLI 封装,主要函数包括:
| 函数 | 职责 |
|---|---|
_save_output(path, payload) |
将输出保存到 JSON 文件 |
run_interactive_mode(agent, output) |
运行交互式问答 |
run_single_question(agent, question, max_iterations, output) |
运行单次问题 |
build_parser() |
构建 argparse 参数解析器 |
main(argv) |
CLI 主入口 |
测试显示 CLI 默认进入 interactive Kimi 模式,也支持命令行参数覆盖;离线 CLI 可写入 UTF-8 JSON,且不需要 API 凭证;未提供 query 时会使用默认问题。
3.4 quickstart.py:新手快速体验
quickstart.py 面向第一次运行项目的用户,包含:
Colors:终端颜色类。print_colored(text, color):彩色输出。print_banner():打印欢迎横幅。check_api_key():检查 API Key。demo_search(agent):演示搜索。interactive_mode(agent):交互模式。main():快速开始入口。
它更强调体验流程,而不是完整参数控制。
3.5 examples.py:高级用法示例
examples.py 定义 AdvancedWebSearchAgent(WebSearchAgent),扩展了以下能力:
| 方法 | 作用 |
|---|---|
batch_search(questions) |
批量处理多个问题 |
search_with_context(question, context) |
带上下文搜索 |
comparative_search(items, aspect) |
对多个对象做对比搜索 |
fact_check(statement) |
对陈述做事实核查 |
模块还提供多个示例函数:
example_basic_search()example_batch_search()example_contextual_search()example_comparative_search()example_fact_check()example_research_assistant()main()
测试显示,失败兜底答案会被标记为 error,批量搜索会覆盖所有失败 fallback。
3.6 run_experiment_1_2.py:实验执行与验收
该脚本用于"Run Experiment 1-2 through Kimi K3's official Formula web-search tool"。摘要中的函数包括:
| 函数 | 作用 |
|---|---|
git_value(*args) |
获取 git 相关值 |
response_ids(turns) |
提取响应 ID |
fiber_ids(turns) |
提取 Fiber ID |
has_web_search_declaration(tools) |
判断工具声明是否包含 web search |
usage(turns) |
汇总 token/usage 信息 |
validate(payload) |
校验实验 payload |
write_json(path, value) |
写入 JSON |
run_once(model, timeout) |
执行一次实验 |
main() |
CLI 入口 |
测试文件揭示了关键验收规则:
- 只接受真实成功的 Fiber receipts。
- 验收要求存在不同的、顺序发生的 Formula Fibers 与链接。
- 如果一轮搜索中产生两个 Fibers,会被拒绝。
这说明实验关注的是"独立问题中多轮顺序搜索"的真实性,而不是任意工具调用次数。
3.7 tests/:测试体系
测试目录结构清晰:
| 测试文件 | 覆盖范围 |
|---|---|
conftest.py |
共享 fixtures:隔离 Provider 环境、阻断外部网络、构造 tool call/choice |
test_agent.py |
ReAct 格式化、工具定义、Fiber 转发、Agent 循环、异常处理 |
test_config.py |
模型映射、Provider 选择、key 解析 |
test_examples.py |
高级 Agent helper、失败分类 |
test_experiment_1_2.py |
Fiber 验收规则 |
test_main.py |
CLI 参数、离线分发、JSON 输出 |
test_offline_demo.py |
离线 demo 的确定性与输出模式 |
特别值得学习的是 conftest.py 中的 block_external_network(monkeypatch),它表明测试默认避免真实外网调用,适合 CI 与离线验证。
3.8 核心原理通俗解读
可以把这个 Agent 理解成一个"会自己查资料的研究助理":
- 用户提出问题。
- 助理先判断:我现有知识够不够?
- 如果不够,就按照官方提供的
web_search表单填写查询词。 - 系统把表单原样交给 Moonshot 的搜索 Fiber。
- 搜索结果返回后,助理阅读结果。
- 如果信息仍不足,就继续查;如果足够,就写最终答案。
这里的关键不是"调用了搜索",而是模型自己决定:
- 要不要搜索。
- 搜索什么。
- 搜几次。
- 何时停止。
- 如何综合结果。
工程代码的职责则是保证这个循环可靠执行:工具声明准确、参数不被篡改、失败可识别、过程可追踪、结果可验证。
4. 关键数据流与调用链
根据 README 与 agent.py 摘要,核心在线调用链如下:
text
用户问题
↓
WebSearchAgent.search_and_answer()
↓
_get_system_prompt()
↓
_get_tools()
↓
GET /v1/formulas/moonshot/web-search:latest/tools
↓
POST /v1/chat/completions
↓
模型返回 web_search tool call
↓
_execute_formula(name, raw_arguments)
↓
POST /v1/formulas/moonshot/web-search:latest/fibers
↓
仅接受 HTTP 成功且 status == "succeeded" 的结果
↓
将 context.output 或 encrypted output 作为 tool result 返回模型
↓
模型继续推理 / 再次搜索 / 输出最终答案
4.1 在线搜索主流程
步骤 1:获取工具声明
Agent 不手写 web_search schema,而是请求:
text
GET /v1/formulas/moonshot/web-search:latest/tools
目的是获取 Moonshot 权威的标准 function declaration。这样可以避免本地 schema 与平台不一致。
步骤 2:发起模型推理
Agent 将系统提示词、历史消息、用户问题和工具声明一起发送到:
text
POST /v1/chat/completions
模型决定是否调用工具、调用几次,以及参数是什么。
步骤 3:执行 Formula Fiber
对于每个模型工具调用,Agent 将返回的:
name- raw serialized
arguments
原样转发到:
text
POST /v1/formulas/moonshot/web-search:latest/fibers
这是 README 中强调的"Exact Formula route",中间层不应擅自改参数。
步骤 4:过滤有效结果
只有满足以下条件的 Fiber 才能作为 observation:
- HTTP 请求成功。
- Fiber
status == "succeeded"。
随后读取:
context.output- 或 encrypted output
具体字段结构未在摘要中完整展开,需以源码和实际响应为准。
步骤 5:多轮迭代
模型拿到 observation 后可能:
- 继续调用
web_search。 - 要求更多信息。
- 直接生成最终答案。
循环由 max_iterations 限制,防止无限搜索。
4.2 离线 Demo 调用链
agent.py 提供 run_offline_demo(question, verbose),测试表明它:
- 返回完整且确定的 trace。
- verbose 模式会打印每一步。
- quiet 模式不打印内容。
离线流程不依赖真实 API,适合在没有 API Key 时理解 ReAct 循环,也适合测试 CLI 输出。
4.3 CLI 调用链
main.py 的典型调用链:
text
python main.py [参数]
↓
build_parser()
↓
main(argv)
↓
根据参数选择:
├─ run_single_question()
└─ run_interactive_mode()
↓
必要时调用 agent.search_and_answer()
↓
可选 _save_output() 写入 JSON
quickstart.py 的调用链更偏引导式:
text
python quickstart.py
↓
print_banner()
↓
check_api_key()
↓
创建 WebSearchAgent
↓
demo_search() 或 interactive_mode()
4.4 实验证据生成链路
run_experiment_1_2.py 的链路可概括为:
text
main()
↓
run_once(model, timeout)
↓
收集 turns / response IDs / fiber IDs / usage
↓
validate(payload)
↓
write_json() 写入 evidence.json
↓
生成 evidence.sha256
↓
validation/ 目录保存可追溯证据
4.5 流程易错节点标注
| 节点 | 易错点 | 后果 | 建议 |
|---|---|---|---|
| 工具声明获取 | 手写或缓存旧 schema | 工具调用失败或行为不一致 | 通过 _get_tools() 拉取官方声明 |
| 参数转发 | 修改模型生成的 arguments | 破坏官方 Formula 路由,结果不可复现 | 原样转发 raw serialized arguments |
| Fiber 判定 | 只看 HTTP 200,不看 status |
可能接受失败搜索结果 | 同时校验 HTTP 成功与 status == "succeeded" |
| 多轮循环 | 不限制迭代次数 | 成本失控或死循环 | 使用 max_iterations |
| 工具结果回填 | 未把 observation 加入 messages | 模型无法基于搜索结果回答 | 按 chat completions tool message 规范回填 |
| 异常 JSON | 工具参数 JSON 损坏 | Agent 循环崩溃 | 测试中已覆盖 malformed JSON,应保留错误处理 |
| 实验验收 | 把同一轮两个 Fiber 当成多轮搜索 | 实验结论无效 | 遵循 test_experiment_1_2.py 的顺序 Fiber 规则 |
| 离线/在线混淆 | 无 Key 时直接运行在线模式 | 启动失败 | 使用 offline demo 或先配置 API Key |
5. 关键类/函数速查表
5.1 agent.py
| 名称 | 类型 | 摘要 |
|---|---|---|
WebSearchAgent |
类 | Kimi Web Search Agent 核心类 |
WebSearchAgent.__init__ |
方法 | 初始化 API Key、base URL、model、verbose |
WebSearchAgent._emit |
方法 | 发出 trace step |
WebSearchAgent._get_tools |
方法 | 获取官方 Formula 工具声明 |
WebSearchAgent._execute_formula |
方法 | 执行 Formula Fiber |
WebSearchAgent._get_system_prompt |
方法 | 返回系统提示词 |
WebSearchAgent._chat |
方法 | 调用 chat completions |
WebSearchAgent.search_and_answer |
方法 | 执行多轮搜索与回答主循环 |
WebSearchAgent.clear_history |
方法 | 清空历史 |
WebSearchAgent.get_conversation_history |
方法 | 获取对话历史 |
WebSearchAgent.get_trace |
方法 | 获取 trace |
_reasoning_safe_temperature |
函数 | 为推理模型限制 temperature |
format_trace_step |
函数 | 格式化并截断 trace step |
search_impl |
函数 | 搜索实现相关逻辑 |
is_failure_answer |
函数 | 判断失败兜底答案 |
run_offline_demo |
函数 | 运行确定性离线演示 |
main |
函数 | agent.py 入口 |
5.2 config.py
| 名称 | 类型 | 摘要 |
|---|---|---|
Config |
类 | 配置读取与校验 |
Config.validate |
类方法 | 校验配置 |
Config.get_api_key |
类方法 | 获取 API Key |
5.3 main.py
| 名称 | 类型 | 摘要 |
|---|---|---|
_save_output |
函数 | 保存 JSON 输出 |
run_interactive_mode |
函数 | 运行交互模式 |
run_single_question |
函数 | 运行单次问题 |
build_parser |
函数 | 构建 CLI parser |
main |
函数 | CLI 主入口 |
5.4 quickstart.py
| 名称 | 类型 | 摘要 |
|---|---|---|
Colors |
类 | 终端颜色定义 |
print_colored |
函数 | 打印彩色文本 |
print_banner |
函数 | 打印启动横幅 |
check_api_key |
函数 | 检查 API Key |
demo_search |
函数 | 演示搜索 |
interactive_mode |
函数 | 快速交互模式 |
main |
函数 | quickstart 入口 |
5.5 examples.py
| 名称 | 类型 | 摘要 |
|---|---|---|
AdvancedWebSearchAgent |
类 | 扩展批量、上下文、对比、事实核查能力 |
batch_search |
方法 | 批量搜索多个问题 |
search_with_context |
方法 | 带上下文搜索 |
comparative_search |
方法 | 多对象对比搜索 |
fact_check |
方法 | 事实核查 |
example_basic_search |
函数 | 基础搜索示例 |
example_batch_search |
函数 | 批量搜索示例 |
example_contextual_search |
函数 | 上下文搜索示例 |
example_comparative_search |
函数 | 对比搜索示例 |
example_fact_check |
函数 | 事实核查示例 |
example_research_assistant |
函数 | 研究助理示例 |
main |
函数 | examples 入口 |
5.6 run_experiment_1_2.py
| 名称 | 类型 | 摘要 |
|---|---|---|
git_value |
函数 | 获取 git 信息 |
response_ids |
函数 | 提取响应 ID |
fiber_ids |
函数 | 提取 Fiber ID |
has_web_search_declaration |
函数 | 检查是否包含 web search 声明 |
usage |
函数 | 汇总 usage |
validate |
函数 | 校验实验 payload |
write_json |
函数 | 写入 JSON 文件 |
run_once |
函数 | 执行一次实验 |
main |
函数 | 实验脚本入口 |
6. 如何运行与调试(基于摘要推断,无法确定则说明)
以下步骤严格依据 README 摘要与模块摘要整理。未在摘要中出现的参数、环境变量名、输出内容不做编造。
6.1 环境准备
步骤 1:进入仓库根目录
README 推荐从书籍仓库根目录使用 Chapter 1 共享环境。
执行目的:使用统一依赖环境,避免子项目单独安装导致版本不一致。
README 给出的推荐命令:
bash
uv sync --locked --extra ch1
如果没有安装 uv,README 提供 pip fallback:
bash
python -m pip install -e ".[ch1]"
步骤 2:激活虚拟环境
macOS/Linux:
bash
source .venv/bin/activate
Windows PowerShell:
powershell
.venv\Scripts\Activate.ps1
Windows cmd:
bat
.venv\Scripts\activate.bat
执行目的:确保后续 python、pytest 使用项目依赖。
步骤 3:进入本实验目录
bash
cd chapter1/web-search-agent
如果不使用根目录共享环境,README 也说明仍支持单项目兼容路径:
bash
python -m pip install -r requirements.txt
执行目的:安装当前实验目录的依赖。
可能生成或变化的内容:
- Python 虚拟环境目录,通常为
.venv/。 - 本地 pip 包缓存。
- 若使用 editable install,可能生成 egg-info/构建缓存,具体取决于环境。
6.2 配置 API Key
仓库包含 env.example,说明项目通过环境变量或 .env 配置 API Key。config.py 导入了 dotenv,因此支持从 .env 加载配置。
建议操作:
- 复制
env.example为.env。 - 根据文件中的提示填入 Kimi/Moonshot API Key 或其他 Provider 配置。
- 不要把
.env提交到版本控制。
由于解析摘要未展示 env.example 的具体字段,本文不编造变量名。配置字段请以 env.example、config.py 源码和 README 未展示部分为准。
执行目的:让在线 Agent 能通过 Config.get_api_key 或相关配置逻辑获取凭证。
6.3 运行快速开始
入口文件:
bash
python quickstart.py
执行目的:
- 打印欢迎横幅。
- 检查 API Key。
- 创建 Agent。
- 进入演示搜索或交互模式。
正常控制台输出特征:
- 可能出现彩色 banner,因为
quickstart.py定义了Colors与print_colored。 - 如果 API Key 缺失,
check_api_key()应提示配置问题。 - 如果配置正确,会进入 demo 或交互流程。
运行后是否生成文件:
- 摘要未显示
quickstart.py会写文件。 - 通常应只产生控制台输出;若实际脚本支持输出文件,需以源码为准。
6.4 运行主 CLI
入口文件:
bash
python main.py
根据测试摘要,不带参数时默认进入 interactive Kimi 模式。
执行目的:启动交互式 Web Search Agent。
正常控制台输出特征:
- 进入交互模式后,程序应等待用户输入问题。
- Agent 可能打印 trace、最终答案或错误信息。
- verbose 行为取决于 CLI 参数和 Agent 初始化配置。
单次问题模式
main.py 提供 run_single_question(agent, question, max_iterations, output),说明 CLI 应支持传入问题、最大迭代次数和输出路径。
参数解析由 build_parser() 定义,但解析摘要未列出完整参数名。可通过以下方式查看实际帮助:
bash
python main.py --help
执行目的:在不阅读源码的情况下确认 CLI 支持的参数。
保存 JSON 输出
main.py 包含 _save_output(path, payload),说明可将结果保存为 JSON。若 CLI 暴露了 output 参数,典型形式可能类似:
bash
python main.py <问题参数> <你的问题> <输出参数> result.json
注意:以上只是基于函数职责的形式推断,具体参数名未在解析摘要中发现,请以 python main.py --help 为准。
运行后可能生成:
- 指定路径的 JSON 文件。
- 内容应包含问题、答案、trace 或相关元数据,具体结构以源码为准。
6.5 运行离线 Demo
测试显示离线 CLI 可写入 UTF-8 JSON,且不需要 API 凭证;未提供 query 时使用默认问题。
agent.py 也提供:
python
run_offline_demo(question, verbose)
可通过 agent.py 或 main.py 的离线参数运行。由于摘要未列出明确 CLI 参数,建议先查看:
bash
python agent.py --help
python main.py --help
执行目的:
- 在没有 API Key 的情况下理解 ReAct 流程。
- 验证本地环境和 CLI 输出是否正常。
- 用于自动化测试,避免外部网络依赖。
正常控制台输出特征:
- verbose 模式:打印每个 trace step。
- quiet 模式:不打印内容。
- 返回结果应是完整且确定性的 trace。
运行后可能生成:
- 如果传入 output 路径,会生成 UTF-8 JSON 文件。
- 文件名取决于命令参数。
6.6 运行高级示例
入口文件:
bash
python examples.py
执行目的:体验以下高级能力:
- 基础搜索。
- 批量搜索。
- 上下文搜索。
- 对比搜索。
- 事实核查。
- 研究助理流程。
正常控制台输出特征:
- 每个
example_*函数可能打印对应示例标题与结果。 - 批量搜索会对多个问题分别执行。
- 失败兜底答案应被标记为 error。
运行后是否生成文件:
- 摘要未显示
examples.py会保存文件。 - 若需要保存,可参考
main.py的_save_output自行扩展。
6.7 运行实验 1-2
入口文件:
bash
python run_experiment_1_2.py
该脚本使用 argparse,但摘要未列出完整参数。建议先执行:
bash
python run_experiment_1_2.py --help
执行目的:
- 通过 Kimi K3 官方 Formula web-search tool 运行实验。
- 收集 turns、response IDs、fiber IDs、usage。
- 验证是否满足多轮顺序搜索要求。
- 写入 evidence 文件。
运行后可能生成:
validation/下新的时间戳目录。evidence.json。evidence.sha256。validation/latest.json可能被更新。
正常结果特征:
- evidence 中应包含真实成功的 Fiber receipts。
- 不同的、顺序发生的 Formula Fibers 与链接。
- 不应把同一轮中的两个 Fibers 当作合格多轮搜索。
无实测结果:解析摘要未提供本次运行的实验数据、耗时、token 用量或准确率,因此这里不编造实验结果。可预留如下结果对比模块:
| 实验项 | 预期 | 本次实测 |
|---|---|---|
是否调用官方 web_search 声明 |
是 | 未实测 |
| Fiber 是否 succeeded | 是 | 未实测 |
| 是否产生多轮顺序 Fibers | 是 | 未实测 |
| evidence 是否通过校验 | 是 | 未实测 |
| token/耗时 | 未在摘要中发现 | 未实测 |
6.8 运行测试
仓库包含 pytest.ini,测试框架为 pytest。
在 chapter1/web-search-agent 目录下执行:
bash
pytest
执行目的:
- 验证 Agent 循环、配置、CLI、示例、实验验收逻辑是否正常。
- 在不调用真实外部网络的情况下完成大部分单元测试。
正常测试特征:
conftest.py提供外部网络阻断 fixture。- 测试覆盖 malformed tool arguments、迭代上限、空答案、部分答案等边界情况。
- 离线 demo 测试可验证确定性输出。
运行后通常生成:
- pytest 控制台结果。
- 若启用 coverage,可能生成覆盖率文件;摘要未显示 coverage 配置,因此默认不应假设存在。
6.9 调试建议
建议 1:先跑离线 demo
如果在线 API 不通,先运行离线模式,确认 Python 环境、CLI 参数和 JSON 输出正常。
建议 2:查看 trace
WebSearchAgent.get_trace() 可获取执行轨迹。调试时重点查看:
- 模型是否请求了
web_search。 - 工具名是否为官方声明中的名称。
- arguments 是否是合法 JSON。
- Fiber 是否 succeeded。
- observation 是否正确回填。
- 是否达到
max_iterations。
建议 3:打开 verbose
WebSearchAgent.__init__ 支持 verbose 参数。verbose 模式有助于观察每一步,但生产环境或批量任务中应谨慎使用,避免日志过长。
建议 4:使用 mock 复现问题
tests/test_agent.py 中已有 FakeResponse、make_choice、make_tool_call 等测试工具。排查在线问题时,可先用 mock 构造模型响应,隔离 API 网络问题。
建议 5:校验实验 evidence
如果实验结果异常,优先检查:
- Fiber receipt 是否真实成功。
- Fiber ID 是否不同。
- Fibers 是否来自不同顺序轮次。
- 是否存在同一轮两个 Fibers。
evidence.sha256是否与evidence.json匹配。
7. 可扩展点与二次开发建议
7.1 扩展新的官方工具
当前项目只接入 Moonshot 官方 web_search。如果未来要支持其他 Formula 工具,可参考现有模式:
- 在
_get_tools()中获取对应工具声明。 - 在
_execute_formula()中按工具名转发到对应 Fiber endpoint。 - 保持模型返回的
name与 raw arguments 原样传递。 - 对 Fiber 结果执行严格状态校验。
- 为新工具补充单元测试。
注意:摘要未显示通用多工具注册机制,因此当前实现可能与 moonshot/web-search:latest 耦合较深。二次开发时可抽象出:
ToolProviderToolDeclarationFetcherToolExecutorToolResultValidator
7.2 增强 trace 与可观测性
现有 trace 已记录 Agent 步骤。可扩展方向包括:
- 将 trace 输出为 JSONL,方便日志系统采集。
- 为每一步增加时间戳、耗时、token usage。
- 记录 Fiber request ID,便于向平台排查。
- 对长 observation 做结构化截断与原文存档。
- 接入 OpenTelemetry 或 Langfuse 等观测平台。
需要注意 format_trace_step(step, max_len) 已经有截断逻辑,扩展时不要丢失关键证据。
7.3 增强错误恢复
测试已覆盖以下异常:
- malformed tool arguments JSON。
- 迭代上限。
- 空答案或截断答案。
- 可读错误返回。
可继续增强:
- Fiber 超时重试。
- 429/5xx 指数退避。
- 搜索结果为空时让模型换查询词。
- 失败答案分类与用户友好提示。
- 将网络异常与模型异常区分开。
7.4 扩展高级研究能力
AdvancedWebSearchAgent 已提供批量、上下文、对比和事实核查能力。可进一步增加:
- 多语言搜索策略。
- 来源可信度评分。
- 引用链接与证据片段对齐。
- 报告生成:摘要、对比表、参考文献。
- 长任务断点续跑。
- 并发批量搜索与限流。
开发并发能力时要注意模型 API 限流,不能简单无限制并发。
7.5 抽象 Provider 配置
config.py 测试显示项目已处理 Kimi、OpenRouter、模型映射和 key 选择。可进一步抽象:
ProviderConfigModelRouterCredentialResolver
这样可以把"模型名映射""base URL 选择""key 优先级"从 Agent 主逻辑中剥离。
7.6 方案优劣对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 官方 Formula web-search | 工具声明权威、行为贴近平台、支持原生 Agent 能力 | 依赖 Moonshot 托管搜索,自定义空间有限 | 教学演示、官方能力验证、生产搜索 Agent |
| 自定义搜索工具 | 可接入任意搜索引擎、可控排序与过滤 | schema 和执行逻辑需自行维护,复杂度高 | 企业内部搜索、垂直领域检索 |
| 单次搜索后回答 | 实现简单、成本低 | 复杂问题容易证据不足 | 简单事实查询 |
| 多轮 ReAct 搜索 | 证据更充分、能处理复杂问题 | 成本更高、需控制迭代与失败 | 研究助理、事实核查、对比分析 |
| 离线 demo | 无 API 成本、确定性强、适合测试 | 不反映真实网络结果 | 教学、CI、UI 调试 |
| 在线实验 evidence | 结果可追溯、可校验 | 需要真实 API 与网络,成本不可完全避免 | 论文/课程实验、能力验收 |
7.7 落地场景
该架构可迁移到以下场景:
- 企业知识库检索 Agent:将 Formula web-search 替换为内部搜索工具,保留 ReAct 循环。
- 市场研究助理 :使用
comparative_search思路对比产品、价格、评价。 - 事实核查系统:对用户陈述进行多轮搜索,并保留证据链接。
- 技术文档问答:接入官方文档搜索,生成带引用的答案。
- 教学实验平台:用 trace 展示 Agent 的思考、行动和观察过程。
- 自动化报告生成:批量收集资料,再汇总为结构化报告。
7.8 面试考点提炼
| 考点 | 对应项目实现 |
|---|---|
| ReAct 循环 | think → act → observe → answer |
| 工具调用协议 | 动态获取 function declaration,模型返回 tool call |
| 官方工具接入 | GET tools、POST chat/completions、POST fibers |
| 多轮 Agent 控制 | max_iterations 防止无限循环 |
| 工具参数安全 | raw arguments 原样转发,不擅自修改 |
| 失败处理 | Fiber 状态校验、异常 JSON、失败答案分类 |
| 可观测性 | trace、conversation history |
| 测试设计 | mock 网络、构造 tool call、验证循环边界 |
| 实验可复现 | evidence.json 与 evidence.sha256 |
| Provider 抽象 | API key、base URL、model mapping、OpenRouter 选择 |
8. 常见问题与排查清单
8.1 API Key 未配置或读取失败
- 【故障现象】 运行
quickstart.py、main.py或在线示例时提示缺少 API Key,或无法创建 Agent。 - 【根因】 未创建
.env、环境变量未生效、虚拟环境未激活,或填入的 key 不属于当前 Provider。 - 【修复方案】
- 确认已从
env.example复制出.env。 - 按
env.example与config.py要求填写正确变量。 - 激活当前虚拟环境后再运行。
- 如使用 OpenRouter,确认相关 key 与模型映射配置正确。
- 确认已从
8.2 离线模式与在线模式混淆
- 【故障现象】 没有 API Key 时运行主程序失败,或误以为离线 demo 会访问真实网络。
- 【根因】 未区分在线 Kimi 模式和离线 deterministic demo。
- 【修复方案】
- 无网络或无 Key 时运行离线 demo。
- 通过
python agent.py --help或python main.py --help查看离线参数。 - 需要真实搜索结果时再配置 API Key 并运行在线模式。
8.3 工具声明获取失败
- 【故障现象】 Agent 在
_get_tools()阶段报错,无法获取web_searchfunction declaration。 - 【根因】 网络不可达、base URL 错误、API Key 无效、平台接口变更或权限不足。
- 【修复方案】
- 检查
Config中的 base URL。 - 确认 API Key 有权限访问 Formula tools。
- 检查本地网络、代理和防火墙。
- 不要手写旧 schema 绕过,应优先修复官方声明获取流程。
- 检查
8.4 Fiber 调用失败
- 【故障现象】 模型请求了
web_search,但工具执行阶段报错,或 Agent 返回可读错误。 - 【根因】 Fiber endpoint 不可达、参数格式异常、搜索服务失败、HTTP 非 2xx,或 Fiber
status不是succeeded。 - 【修复方案】
- 查看 trace 中记录的工具名和 raw arguments。
- 确认参数是模型返回的原始序列化 JSON,未被中间层破坏。
- 检查 Fiber 响应中的状态码和
status字段。 - 对超时或临时错误增加重试,但不要把失败 Fiber 当成成功 observation。
8.5 工具参数 JSON 解析失败
- 【故障现象】 Agent 循环遇到 malformed tool arguments JSON,测试中也专门覆盖该场景。
- 【根因】 模型返回的 arguments 格式不完整,或中间处理破坏了 JSON 字符串。
- 【修复方案】
- 保留现有异常捕获逻辑,避免整个 Agent 崩溃。
- 将解析错误作为 observation 返回模型,让模型修正。
- 不要在转发前"修复"模型参数,以免破坏官方路由。
- 在 trace 中记录原始 arguments,便于排查。
8.6 Agent 一直搜索或达到迭代上限
- 【故障现象】 Agent 多轮调用搜索仍不回答,最终触发 iteration limit。
- 【根因】 问题过于复杂、搜索结果不充分、模型未判断证据足够,或
max_iterations设置过小。 - 【修复方案】
- 适当增大
max_iterations。 - 在系统提示词中明确停止条件。
- 检查 Fiber 输出是否被正确回填。
- 对空结果提示模型改写查询词。
- 设置成本与轮数上限,避免无限调用。
- 适当增大
8.7 搜索结果没有被模型使用
- 【故障现象】 工具调用成功,但最终答案与搜索结果无关,或像没有看到 observation。
- 【根因】 tool result 没有按 chat completions 规范加入 messages,或 trace 中 observation 被截断/丢失。
- 【修复方案】
- 检查每次 Fiber 输出后是否追加了对应的 tool message。
- 确认 tool call ID 与 tool result ID 对应。
- 检查
format_trace_step的截断是否只影响展示,不影响模型输入。 - 通过
get_conversation_history()检查消息序列。
8.8 CLI 参数不确定
- 【故障现象】 不知道如何传入问题、输出路径或最大迭代次数。
- 【根因】 解析摘要未完整列出 argparse 参数。
- 【修复方案】
- 运行
python main.py --help。 - 运行
python agent.py --help。 - 运行
python run_experiment_1_2.py --help。 - 查看
build_parser()源码确认参数名。
- 运行
8.9 测试无法运行或导入失败
- 【故障现象】 执行
pytest时报模块找不到、依赖缺失或配置错误。 - 【根因】 未安装依赖、未激活虚拟环境、运行目录不正确,或没有使用 Chapter 1 共享环境。
- 【修复方案】
- 在仓库根目录执行 README 推荐的
uv sync --locked --extra ch1。 - 或在本目录执行
python -m pip install -r requirements.txt。 - 激活
.venv。 - 在
chapter1/web-search-agent目录下执行pytest。
- 在仓库根目录执行 README 推荐的
8.10 测试触发外部网络
- 【故障现象】 单元测试尝试访问真实 Kimi/Moonshot 接口。
- 【根因】 测试没有使用网络隔离 fixture,或本地环境变量影响了 Provider 初始化。
- 【修复方案】
- 使用
tests/conftest.py中的isolate_provider_environment和block_external_network。 - 检查是否有本地
.env在测试时被自动加载。 - 对外部调用继续使用
unittest.mock或 fake response。 - 不要在单元测试中依赖真实网络结果。
- 使用
8.11 实验校验失败
- 【故障现象】
run_experiment_1_2.py生成结果但validate(payload)不通过。 - 【根因】 Fiber 不是真实成功状态、Fiber ID 不满足顺序要求、同一轮中出现两个 Fibers,或 evidence 被修改。
- 【修复方案】
- 检查每个 Fiber 是否 HTTP 成功且
status == "succeeded"。 - 确认多轮搜索是独立顺序发生,而不是一轮中并发两个 Fibers。
- 检查 response IDs、fiber IDs 和链接是否完整。
- 重新运行实验并生成新的
evidence.json与evidence.sha256。 - 不要手动编辑 evidence 文件。
- 检查每个 Fiber 是否 HTTP 成功且
8.12 Windows 激活脚本无法执行
- 【故障现象】 Windows PowerShell 执行
.venv\Scripts\Activate.ps1时提示脚本被禁止。 - 【根因】 PowerShell 执行策略限制。
- 【修复方案】
- 使用 cmd 执行
.venv\Scripts\activate.bat。 - 或按组织允许的方式调整 PowerShell 执行策略。
- 也可直接使用
.venv\Scripts\python.exe运行脚本,避免激活。
- 使用 cmd 执行
8.13 JSON 输出乱码
- 【故障现象】 保存的 JSON 文件中中文显示为转义字符或乱码。
- 【根因】 写入时未指定 UTF-8,或
json.dump启用了 ASCII 转义。 - 【修复方案】
- 参考
test_main.py中"offline CLI writes UTF-8 JSON"的预期。 - 保存文件时使用 UTF-8 编码。
- 如需可读中文,确认
json.dump(..., ensure_ascii=False)是否按项目设计启用。 - 不要修改与测试预期冲突的输出格式。
- 参考
8.14 verbose 日志过长
- 【故障现象】 控制台输出大量搜索内容,影响阅读或泄露敏感信息。
- 【根因】
verbose会打印 trace step,而搜索结果可能很长。 - 【修复方案】
- 普通使用时关闭 verbose。
- 依赖
format_trace_step(step, max_len)做截断展示。 - 将完整 trace 写入受控日志文件,而不是直接打印。
- 对敏感字段做脱敏。