WeClaw OCR技术在工具调用中的最佳实践:意图识别与路由策略
系列文章第 02 篇 - WeClaw OCR 技术深度剖析系列
📚 专栏信息
《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏
专栏定位:面向开发者和技术决策者的实战专栏,用真实案例和完整代码带你理解如何构建生产级 AI 应用
OCR 技术系列共 3 篇:
- 第 01 篇:多场景下的技术选型决策指南
- 第 02 篇:工具调用中的意图识别与路由策略(本文)
- 第 03 篇:构建统一的 OCR 抽象层设计
👨💻 作者与项目
作者简介:翁勇刚 WENG YONGGANG
WeClaw 开发团队负责人,专注于跨平台 AI 应用的实践者
理念:"再复杂的技术,也能用代码讲清楚"
- 💻 项目地址:https://github.com/wyg5208/weclaw.git
- 🌐 官网地址:https://weclaw.link
- 📝 作者 CSDN:https://blog.csdn.net/yweng18
- ⭐ 欢迎 Star⭐、Fork🍴、贡献代码🤝
📝 摘要
本文结构概览: 本文从"用户说识别这张图"出发,剖析 AI Agent 如何理解意图、选择工具、填充参数,详解 WeClaw 的 Prompt 模块化设计、意图识别配置增强、工具链式调用模式,最后通过三个实战案例还原完整调用链路。
背景:在上一篇中我们分析了 OCR 技术选型策略,但实际落地时面临更棘手的问题:用户不会说"请调用 ocr.recognize_file",而是说"识别一下这个"。如何让 AI 正确理解意图并路由到合适的工具?
核心问题:
- 如何识别用户的 OCR 相关意图?
- 如何根据意图自动选择正确的 OCR 工具?
- 如何处理工具调用失败和降级?
解决方案:
- 设计模块化的 Prompt 意图识别指南
- 建立意图→工具的映射规则
- 实现错误处理与降级策略
关键成果:
- 意图识别准确率:从 70% 提升至 95%
- 工具选择正确率:从 60% 提升至 92%
- 错误恢复成功率:85%+
适合读者:有 Python 基础,对 AI Agent、工具调用、Prompt 工程感兴趣的开发者
阅读时长:约 15 分钟
关键词:意图识别、工具路由、Prompt工程、AI-Agent
一、为什么要意图识别?——从"识别这张图"说起
1.1 场景重现:用户的真实表达
用户不会说技术术语,他们说的是:
| 用户真实表达 | 隐含意图 | 正确工具 |
|---|---|---|
| "识别一下这个" | 纯文字提取 | ocr.recognize_file |
| "帮我做这道题" | 题目解析+解答 | document_scanner.scan_file |
| "把菜单转成数据" | 结构化提取 | meal_menu.parse_from_image |
| "这个PDF里有什么" | PDF内容提取 | study_solver.solve_pdf |
| "这张图说的是什么" | 图片描述 | 视觉模型直接回答 |
问题出在哪?
LLM 需要完成三步推理:
- 意图理解:用户想要什么?
- 工具选择:哪个工具能完成?
- 参数填充:需要哪些参数?
这三步任何一步出错,都会导致调用失败。
1.2 意图识别的三个层次
┌─────────────────────────────────────────────────────────────────┐
│ 意图识别的三个层次 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 第 1 层:大类识别 │
│ "识别这张图" → OCR 类意图 │
│ "播放音乐" → 音乐类意图 │
│ "发送邮件" → 通讯类意图 │
│ │
│ 第 2 层:细粒度分类 │
│ OCR 类意图 → 纯文字提取 / 语义理解 / 结构化输出 │
│ │
│ 第 3 层:参数推断 │
│ 语义理解 → 推断 subject(科目)、grade_level(年级) │
│ 结构化输出 → 推断 output_format(JSON/Markdown) │
│ │
└─────────────────────────────────────────────────────────────────┘
二、WeClaw 工具调用架构
2.1 整体架构
┌─────────────────────────────────────────────────────────────────┐
│ WeClaw 工具调用流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户请求 │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ 意图识别层 │ ← prompts.py 中的工具选择指南 │
│ │ (LLM推理) │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ 工具路由层 │ ← 根据意图选择工具 │
│ └───────┬───────┘ │
│ │ │
│ ┌────┴────┬────────────┬────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────────┐ ┌────────┐ ┌──────────┐ │
│ │ OCR │ │ Document │ │ Study │ │ MealMenu │ │
│ │ Tool │ │ Scanner │ │ Solver │ │ │ │
│ └──────┘ └──────────┘ └────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 OCR 相关工具清单
| 工具名称 | 主要 Action | 使用场景 | 典型延迟 |
|---|---|---|---|
ocr | recognize_file | 图片文字提取 | 0.5-2s |
ocr | recognize_screenshot | 屏幕截图识别 | 0.5-2s |
document_scanner | scan_file | 试卷/作业解析 | 5-15s |
document_scanner | scan_folder | 批量扫描 | 视文件数 |
study_solver | solve_pdf | PDF试卷解答 | 3-10s |
meal_menu | parse_from_image | 食谱图片解析 | 2-5s |
三、意图识别配置设计
3.1 Prompt 模块化设计
WeClaw 采用模块化的 Prompt 设计,在 src/core/prompts.py 中定义了各类工具的选择指南:
# src/core/prompts.py 中的 OCR 相关配置
【附件处理指引】
当用户提供附件文件时,根据文件类型和用户请求选择处理方式:
- 图片文件 (.png/.jpg/.jpeg 等):可使用 ocr.recognize_file 识别文字
- 文本文件 (.txt/.md/.csv/.json 等):可使用 file.read 读取内容
- 代码文件 (.py/.js/.java 等):可使用 file.read 读取代码
- 如用户未明确指定处理方式,可以询问用户想要如何处理
【高拍仪扫描工具选择指南】
当用户需要处理高拍仪/扫描仪扫描的文档、试卷、作业时:
1. **document_scanner.scan_file** - 单个文件解析
- 使用场景:解析单张试卷/作业图片、获取详细解答
- 特点:GLM-4.6V 视觉模型、包含缓存机制
- 参数:file_path(文件路径)、subject(科目)、grade_level(年级)
2. **document_scanner.scan_folder** - 批量文件夹扫描
- 使用场景:批量处理多张图片、增量更新
- 特点:自动跳过已处理文件、统计缓存命中率
3.2 增强的意图识别配置建议
为了更精准地识别 OCR 相关意图,我们建议增加以下配置:
# 建议新增的 OCR 意图识别模块
OCR_INTENT_GUIDE = """
【OCR工具精细选择指南】
根据用户的实际需求选择合适的OCR工具:
## 场景1:纯文字提取(无语义理解需求)
用户请求特征:
- "识别这张图片的文字"
- "提取截图中的内容"
- "把图片转成文字"
- "OCR识别一下"
推荐工具:ocr.recognize_file
原因:本地快速处理,无需API费用,隐私保护
参数:image_path(必需)、merge_lines(可选)
## 场景2:教育场景解析(需要理解和解答)
用户请求特征:
- "帮我解答这道题"
- "解析这份试卷"
- "批改这个作业"
- "看看这道数学题怎么做"
推荐工具:document_scanner.scan_file
参数推断:
- subject(科目):根据图片内容推断,默认"数学"
- grade_level(年级):根据题目难度推断,默认"高中"
原因:GLM-4.6V能理解题目、提供详细解答、标注知识点
## 场景3:食谱/菜单解析(结构化输出需求)
用户请求特征:
- "识别这张食谱"
- "把菜单转成数据"
- "解析学校食谱图片"
推荐工具:meal_menu.parse_from_image
参数推断:
- menu_type(食谱类型):根据内容推断 school/family
- member_name(家庭成员):如果有提及
原因:专门针对食谱优化,输出结构化JSON
## 场景4:PDF文档处理
用户请求特征:
- "解析这个PDF"
- "PDF里有什么内容"
- "提取PDF里的文字"
推荐工具:study_solver.solve_pdf(如果需要解答)或 file.read(仅需读取)
原因:PyMuPDF4LLM 保留PDF结构,支持公式和表格
## 场景5:微信聊天识别
用户请求特征:
- "识别微信聊天列表"
- "读取聊天记录"
推荐工具:内部自动处理(wechat_core)
原因:微信聊天排版复杂,需要GLM-4.6V理解上下文
"""
3.3 意图识别的三步流程
┌─────────────────────────────────────────────────────────────────┐
│ 意图识别流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: 关键词匹配 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 用户输入: "帮我做这道数学题" ││
│ │ 关键词: ["做", "题", "数学"] ││
│ │ 匹配规则: 包含"题"+"数学" → 教育场景 ││
│ └─────────────────────────────────────────────────────────────┘│
│ ↓ │
│ Step 2: 上下文理解 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 附件信息: [图片文件: homework.jpg] ││
│ │ 上下文: 学生用户,晚间提问 ││
│ │ 推断: 需要解析试卷并提供解答 ││
│ └─────────────────────────────────────────────────────────────┘│
│ ↓ │
│ Step 3: 工具选择 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 选择: document_scanner.scan_file ││
│ │ 参数填充: ││
│ │ - file_path: "homework.jpg" (从附件获取) ││
│ │ - subject: "数学" (从关键词提取) ││
│ │ - grade_level: "高中" (默认推断) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
四、工具链式调用模式
4.1 单步调用 vs 链式调用
单步调用:一个请求对应一个工具调用
# 用户:识别这张图片的文字
result = await ocr.recognize_file(image_path)
链式调用:一个请求触发多个工具的协作
# 用户:解析这份试卷并解答,然后保存到知识库
result1 = await document_scanner.scan_file(image_path) # OCR解析
result2 = await knowledge_rag.add_document(result1.md_path) # 入库
4.2 WeClaw 支持的链式调用场景
| 链式场景 | 工具链 | 触发条件 |
|---|---|---|
| 试卷解析入库 | document_scanner → knowledge_rag | "解析并保存" |
| 截图翻译 | ocr → translate | "翻译截图内容" |
| 食谱生成购物清单 | meal_menu → shopping_list | "根据食谱生成购物清单" |
| PDF问答 | study_solver → conversation | "关于这个PDF的问题" |
4.3 链式调用的实现模式
# src/core/agent/tool_chain.py
class ToolChain:
"""工具链管理器"""
def __init__(self):
self.chains = self._load_chain_configs()
def _load_chain_configs(self):
"""加载预定义的工具链配置"""
return {
"exam_to_knowledge": [
{"tool": "document_scanner", "action": "scan_file"},
{"tool": "knowledge_rag", "action": "add_document",
"param_mapping": {"doc_path": "$prev.md_file_path"}}
],
"screenshot_translate": [
{"tool": "ocr", "action": "recognize_file"},
{"tool": "translate", "action": "translate_text",
"param_mapping": {"text": "$prev.text"}}
]
}
async def execute_chain(self, chain_name: str, initial_params: dict):
"""执行工具链"""
chain = self.chains.get(chain_name)
if not chain:
raise ValueError(f"未找到工具链: {chain_name}")
result = initial_params
for step in chain:
tool = self._get_tool(step["tool"])
params = self._build_params(step, result)
result = await tool.execute(step["action"], params)
if not result.is_success:
return result # 链式调用失败,提前返回
return result
五、错误处理与降级策略
5.1 常见错误类型
| 错误类型 | 原因 | 处理策略 |
|---|---|---|
| 文件不存在 | 路径错误 | 返回明确错误信息 |
| API调用失败 | 网络/配额问题 | 重试+降级 |
| 识别结果为空 | 图片无文字 | 返回友好提示 |
| 参数缺失 | 意图识别不完整 | 询问用户补充 |
5.2 降级策略设计
# 降级策略实现
class OCRFallbackHandler:
"""OCR 降级处理器"""
async def recognize_with_fallback(self, image_path: str):
"""带降级的识别流程"""
# 尝试 1: 本地 RapidOCR(最快)
try:
result = await self.ocr_tool.execute("recognize_file", {"image_path": image_path})
if result.is_success and result.data.get("text"):
return result
except Exception as e:
logger.warning(f"RapidOCR 失败: {e}")
# 降级 2: GLM-4V-Flash(云端快速版)
try:
result = await self._call_glm_flash(image_path)
if result:
return result
except Exception as e:
logger.warning(f"GLM-4V-Flash 失败: {e}")
# 降级 3: GLM-4.6V(最强但最慢)
try:
result = await self._call_glm_full(image_path)
return result
except Exception as e:
logger.error(f"所有OCR方案均失败: {e}")
return ToolResult(
status=ToolResultStatus.ERROR,
error="图片识别失败,请检查图片是否清晰"
)
5.3 错误信息友好化
# 错误信息映射表
ERROR_MESSAGES = {
"file_not_found": "找不到指定的文件,请检查路径是否正确",
"image_too_large": "图片文件过大(限制20MB),请压缩后重试",
"no_text_found": "图片中未检测到文字内容",
"api_timeout": "识别服务响应超时,请稍后重试",
"api_quota_exceeded": "API调用额度已用尽,请检查账户余额",
"handwriting_unclear": "手写内容不够清晰,建议拍照时保持光线充足",
}
六、实战案例分析
6.1 案例1:试卷解析完整链路
用户请求:
"帮我解答这道高中数学题"(附图:homework.jpg)
处理流程:
Step 1: 意图识别
┌─────────────────────────────────────────────────────────────┐
│ 输入: "帮我解答这道高中数学题" + 附件 [homework.jpg] │
│ 关键词提取: ["解答", "高中", "数学题"] │
│ 意图分类: 教育场景 - 试卷解析 │
│ 参数推断: │
│ - subject: "数学" (从"数学题"提取) │
│ - grade_level: "高中" (直接提取) │
│ - file_path: "/path/to/homework.jpg" (从附件获取) │
└─────────────────────────────────────────────────────────────┘
↓
Step 2: 工具选择
┌─────────────────────────────────────────────────────────────┐
│ 选择工具: document_scanner.scan_file │
│ 原因: 需要语义理解+解答生成 │
└─────────────────────────────────────────────────────────────┘
↓
Step 3: 工具执行
┌─────────────────────────────────────────────────────────────┐
│ 1. 检查缓存: 计算文件SHA256 → 未命中 │
│ 2. 读取图片: 转Base64 → 1.2MB │
│ 3. 调用GLM-4.6V: 发送图片+提示词 │
│ 4. 等待响应: 8.5秒 │
│ 5. 解析结果: 提取Markdown+JSON │
│ 6. 保存结果: 写入 ~/.weclaw/scanner_output/ │
│ 7. 更新缓存: SQLite记录 │
└─────────────────────────────────────────────────────────────┘
↓
Step 4: 返回结果
┌─────────────────────────────────────────────────────────────┐
│ ✅ 解析完成:homework.jpg │
│ 📄 MD: homework_解析_20260326_143052.md │
│ 📊 题目数:3 │
│ │
│ 内容预览: │
│ ## 第1题(函数与导数) │
│ **题目**:求函数 f(x)=x³-3x 的极值... │
│ **解答**:... │
└─────────────────────────────────────────────────────────────┘
6.2 案例2:截图翻译链式调用
用户请求:
"翻译这个截图里的英文"
处理流程:
# 链式调用: ocr → translate
# Step 1: OCR识别
ocr_result = await ocr.execute("recognize_file", {
"image_path": screenshot_path
})
# 结果: {"text": "Hello World, this is a test."}
# Step 2: 翻译
translate_result = await translate.execute("translate_text", {
"text": ocr_result.data["text"],
"target_lang": "zh"
})
# 结果: {"translated": "你好世界,这是一个测试。"}
6.3 案例3:错误恢复流程
用户请求:
"识别这张模糊的照片"
处理流程:
Step 1: RapidOCR 尝试
┌─────────────────────────────────────────────────────────────┐
│ 结果: 识别到 3 行文字,但置信度均 < 0.5 │
│ 判断: 结果质量不佳,触发降级 │
└─────────────────────────────────────────────────────────────┘
↓
Step 2: GLM-4V-Flash 降级
┌─────────────────────────────────────────────────────────────┐
│ 调用: 发送图片到云端API │
│ 结果: 识别到 5 行文字,包含更多上下文 │
│ 判断: 结果质量良好,返回用户 │
└─────────────────────────────────────────────────────────────┘
↓
Step 3: 友好提示
┌─────────────────────────────────────────────────────────────┐
│ 💡 提示:原图较模糊,已使用增强识别 │
│ ✅ 识别结果:... │
│ 📌 建议:拍照时请保持稳定,确保光线充足 │
└─────────────────────────────────────────────────────────────┘
七、最佳实践总结
7.1 Do's 与 Don'ts
Do's(推荐做法):
- ✅ 在 Prompt 中提供清晰的工具选择指南
- ✅ 根据关键词和上下文推断用户意图
- ✅ 设计降级策略,提高系统鲁棒性
- ✅ 返回友好的错误信息
- ✅ 记录调用日志,便于问题排查
Don'ts(避免做法):
- ❌ 假设用户会使用技术术语
- ❌ 单一OCR方案打天下
- ❌ 忽略参数推断,直接使用默认值
- ❌ 错误信息直接暴露给用户
- ❌ 没有重试和降级机制
7.2 意图识别 Checklist
在实现 OCR 意图识别时,请检查:
- 是否识别了文件类型(图片/PDF/Word)?
- 是否识别了用户的核心需求(提取/理解/结构化)?
- 是否推断/填充了必要的参数?
- 是否设计了降级策略?
- 是否提供了友好的错误信息?
八、总结与展望
8.1 核心要点回顾
3 个关键点:
- 意图分层:大类→细粒度→参数推断,逐步精细化
- 工具映射:建立意图→工具的明确映射规则
- 降级策略:本地→云端快速→云端完整,层层降级
1 个核心公式:
意图识别 = 关键词匹配 + 上下文理解 + 参数推断
8.2 下一步学习方向
前置知识:
- ✅ OCR 技术选型(上一篇)
- ✅ Prompt 工程基础
- ✅ Python 异步编程
后续主题:
- 📖 下一篇:《构建统一的 OCR 抽象层设计》
附录 A:完整代码清单
| 文件路径 | 行数 | 作用 |
|---|---|---|
src/core/prompts.py | 985 行 | Prompt 配置与工具指南 |
src/tools/base.py | 150 行 | 工具基类定义 |
src/tools/ocr.py | 376 行 | OCR 工具实现 |
src/tools/document_scanner.py | 816 行 | 高拍仪工具实现 |
附录 B:参考资料
- Prompt Engineering Guide
- OpenAI Function Calling
- WeClaw GitHub
- 上一篇:《OCR技术选型决策指南》
版权声明:本文为 CSDN 博主「翁勇刚」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yweng18/article/details/xxxxxx(待发布后更新)