Agent & RAG 测试工程笔记 01:Tool Calling 跑通 + 本地 PDF 接入(智谱 GLM)

前言:

这一篇只做一件事:

把链路跑通到:模型 → 触发工具调用 → 读取本地 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 链路如果不可观测,很容易"以为调用了,实际没调用"。

须看到这些日志才算跑通:

  1. 模型第一次回复 content

  2. tool_calls(工具名 + arguments)

  3. 工具入参/出参

  4. 模型最终输出(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条)

  1. 运行 python main.py 不报错

  2. 第一次回复须出现 tool_calls

  3. 工具入参须能解析出 file_path

  4. 工具出参必须包含 文件名+摘录(弱追溯证据)

  5. 最终输出必须是 Markdown 大纲结构(# / ## / -)

  6. 每条要点须带(来源:文件名 摘录:...),缺失则判失败

七、踩到的坑

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 非常关键,因为它直接决定三件事:

  1. 模型会不会触发 tool_calls(什么时候该用工具)

  2. 工具返回后模型怎么用(把返回当证据、还是当闲聊素材)

  3. 输出格式能不能稳定可测(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)

相关推荐
木雷坞12 小时前
让 AI 编程助手跑得起项目:Dev Container 实践记录
人工智能
腾讯云开发者13 小时前
港科大郭毅可谈Agentic AI时代的核心命题:人机共生,人不可能退场
人工智能
常丛丛13 小时前
5.6 LangGraph-Edges理解-Agent图的道路系统
人工智能
雪隐13 小时前
个人电脑玩AI-08让5060 Ti给你打工——我拿 Unlimited-OCR扫了 600 页书,然后悟了
人工智能·后端
Coffeeee13 小时前
Prompt要花心思写,与 AI 对话的七个技巧
人工智能·aigc·ai编程
蝎子莱莱爱打怪14 小时前
Claude Code 官宣新升级:子智能体默认后台跑,你边聊它边干活
人工智能
武子康14 小时前
调查研究-206 DeepSeek DSpark 深度解析:大模型推理加速,正在从“模型能力”转向“系统工程”
人工智能·agent·deepseek
甲维斯14 小时前
最佳work模型sonnet5来了,直接就能用!
人工智能
IT_陈寒15 小时前
React hooks 闭包陷阱把我的状态吃掉了,原来问题出在这里
前端·人工智能·后端