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)

相关推荐
NAGNIP8 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab9 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab9 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP13 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年13 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼13 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS14 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区15 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈15 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang15 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx