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)

相关推荐
美酒没故事°1 天前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
AI攻城狮1 天前
用 Obsidian CLI + LLM 构建本地 RAG:让你的笔记真正「活」起来
人工智能·云原生·aigc
鸿乃江边鸟1 天前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
lpfasd1231 天前
基于Cloudflare生态的应用部署与开发全解
人工智能·agent·cloudflare
俞凡1 天前
DevOps 2.0:智能体如何接管故障修复和基础设施维护
人工智能
一轮弯弯的明月1 天前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得
comedate1 天前
[OpenClaw] GLM 5 关于电影 - 人工智能 - 的思考
人工智能·电影评价
财迅通Ai1 天前
6000万吨产能承压 卫星化学迎来战略窗口期
大数据·人工智能·物联网·卫星化学
llilian_161 天前
选择北斗导航卫星信号模拟器注意事项总结 北斗导航卫星模拟器 北斗导航信号模拟器
功能测试·单片机·嵌入式硬件·测试工具·51单片机·硬件工程