Agent & RAG 测试工程笔记 01:Tool Calling 跑通 + 本地 PDF 接入(智谱 GLM)
本篇记录跑通 Tool Calling + 本地 PDF 接入的全过程:先定义 tools schema 与两段关键 prompt(触发工具调用 / 基于证据生成),再让模型在 tool_choice=auto 下自动触发 read_local_pdf(file_path),由代码执行工具读取 PDF 文本并返回“文件名+摘录”作为 evidence,最后将 evidence 回填 message
前言:
这一篇只做一件事:
把链路跑通到:模型 → 触发工具调用 → 读取本地 PDF → 回填工具结果 → 模型生成课程大纲(带引用)。
先不做向量库、不做 embedding,这一步的目标是:先把 Agent 的“工具调用骨架”跑起来,后面再迭代到 RAG。
一、 MVP 目标与边界
1.1 本阶段目标(须满足)
-
工具调用须真实发生(tool_calls 可观测)
-
工具读取本地 PDF(用户输入路径)
-
模型最终输出 Markdown 大纲
-
每条要点必须带 弱追溯引用:
(来源:文件名 摘录:...)
1.2 本阶段不做(先不做)
-
不做 embedding / FAISS / 向量库
-
不做多 Agent / 复杂工作流
-
不做前端嵌入编辑器(先用命令行跑通)
二、项目结构(最小化)
只保留一个文件:main.py
原因:阶段1只验证“工具调用链路 + 数据接入”,结构越简单越容易理解和调试。
三、核心流程(执行链路)
整个程序只有两次模型调用:
3.1 第一次调用:让模型自动决定是否调用工具
-
输入:用户给一个 PDF 路径
-
传入:tools schema(包含
read_local_pdf) -
tool_choice:auto
-
预期:模型输出
tool_calls,触发read_local_pdf(file_path=...)
3.2 执行工具:读取本地 PDF
-
工具入参:
file_path -
工具出参:必须包含
-
文件名(demo.pdf)
-
摘录(从 PDF 抽取文本截断)
-
3.3 第二次调用:禁用工具,只做生成
-
把工具返回内容以
role="tool"回填到 messages -
第二次调用不再传 tools(避免模型再次发起工具调用)
-
预期:生成 Markdown 课程大纲,并强制每条要点带引用
四、可观测日志(
阶段1打印 4 类日志,原因是:Agent 链路如果不可观测,很容易“以为调用了,实际没调用”。
须看到这些日志才算跑通:
-
模型第一次回复 content
-
tool_calls(工具名 + arguments)
-
工具入参/出参
-
模型最终输出(Markdown 大纲)
五、实跑结果(关键输出示例)
成功现象应该长这样:
tool_calls 触发:
tool_calls:
{name: read_local_pdf, arguments: {"file_path":"...demo.pdf"}}
工具确实执行:
工具入参: {"file_path":"...demo.pdf"}
工具出参: 文件: demo.pdf
摘录1: ...
最终输出是大纲且带引用:
# 课程大纲:AI能力演示功能
## 第1章 背景与目标
- 产品需要在教学编辑器内提供AI能力演示功能…(来源:demo.pdf 摘录:…)
...
六、测试视角:本阶段的验收点(6条)
-
运行
python main.py不报错 -
第一次回复须出现 tool_calls
-
工具入参须能解析出 file_path
-
工具出参必须包含 文件名+摘录(弱追溯证据)
-
最终输出必须是 Markdown 大纲结构(# / ## / -)
-
每条要点须带(来源:文件名 摘录:…),缺失则判失败
七、踩到的坑
7.1 CompletionMessage 不能 json.dumps
现象:
-
直接
json.dumps(message)报错:Object of type CompletionMessage is not JSON serializable
修复:
-
只打印你关心的字段:
content/tool_calls,避免 dump 整个对象。
7.2 第二次调用模型“又回到开场白”
现象:
-
工具已经读到 PDF,但最终输出仍然是“我先读取文件内容…”
根因(典型):
-
第二次调用仍带 tools/tool_choice,或第二次 prompt 不够硬,模型再次走工具调用路径
最小修复:
-
第二次调用 禁用 tools(不传 tools)
-
第二次 prompt 强制:基于 tool 返回生成大纲,并禁止再次调用工具
八、阶段1核心逻辑(最短闭环)
-
定义工具(tools schema)+ 约束 prompt
-
工具:
read_local_pdf(file_path) -
约束:遇到需要读 PDF 的场景必须调用工具;输出要 Markdown 大纲+引用
-
-
用户输入本地文件路径
-
你输入:
D:\...\demo.pdf
-
-
第一次模型调用:让模型“决策”是否调用工具
-
tool_choice="auto" -
模型判断“这是 PDF,需要读取内容” → 返回
tool_calls(read_local_pdf, file_path=...)
-
-
程序执行工具:读取 PDF,产出“证据”
-
工具真正读文件
-
返回:
文件名 + 摘录(证据)(弱追溯)
-
-
第二次模型调用:把证据喂回去,让它生成大纲
-
把工具结果以
role="tool"追加进 messages -
第二次调用不再启用 tools(避免它又去调用工具)
-
模型基于证据输出 Markdown 大纲(章节→要点),每条要点带来源摘录
-
这条链路里 prompt 非常关键,因为它直接决定三件事:
-
模型会不会触发 tool_calls(什么时候该用工具)
-
工具返回后模型怎么用(把返回当证据、还是当闲聊素材)
-
输出格式能不能稳定可测(Markdown结构、引用是否齐全)
可以把 prompt 当成测试对象,做最小的 prompt 用例:
-
用例1:输入 pdf 路径 → 断言出现 tool_calls(否则 prompt 不合格)
-
用例2:给一段工具摘录 → 断言输出是 Markdown 且每条要点有引用(否则 second_prompt 不合格)
这是“Agent & RAG 测试工程笔记”的含金量点:
prompt 不是玄学,是可回归的控制面。
prompt模板:
Prompt v0.1 — First(触发工具调用):
你是一个课程大纲助手。
当用户提供的是本地 PDF 文件路径时,你必须调用工具 read_local_pdf(file_path) 来读取内容,禁止凭空编造。
调用工具后先不要生成大纲,等待工具返回。
这段的核心是:必须调工具 + 不要猜 + 等工具结果。
Prompt v0.1 — Second(基于证据生成大纲):
你将收到工具返回的“文件名 + 摘录”。请仅基于这些摘录生成 Markdown 课程大纲:
- 结构:# 课程大纲 -> ## 章节 -> - 要点
- 规模:3~8章,每章3~7要点
- 每条要点末尾必须带(来源:文件名 摘录:...)
禁止再次要求读取PDF,禁止调用任何工具,禁止补充证据中没有的信息。
这段的核心是:只用证据 + 固定结构 + 强制引用 + 禁用工具/禁编造。
九、下一步计划(阶段2)
阶段2做 RAG-lite(仍然不做 embedding):
-
read_local_pdf从“1条摘录”升级为“前 N 页多摘录” -
增加“引用校验/剔除”机制:保证输出稳定可验收
-
然后再进入阶段3:embedding + FAISS topK(真正 RAG)
更多推荐



所有评论(0)