Files
Bubbles/function_call_agent_flow.md
2025-09-25 11:54:16 +08:00

87 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Function Call Agent 消息流程
## 概览
- 所有传入的微信消息都会先由机器人入口处理,并在路由前写入消息记录(`robot.py:190-274`)。
- 使用装饰器注册的函数处理器在运行时组成结构化的注册表,供路由器查询(`function_calls/init_handlers.py:1-9`, `function_calls/handlers.py:37-180`)。
- 路由器优先采用确定性的命令解析,只有在必要时才升级到 LLM 编排循环(`function_calls/router.py:81-200`)。
- LLM 协调器同时支持原生函数调用模型和基于提示词的回退模式,统一返回 `LLMRunResult``function_calls/llm.py:33-186`)。
- 结构化处理器返回 `FunctionResult`,路由器可以直接发送回复或将 JSON 结果反馈给 LLM`function_calls/spec.py:12-36`)。
## 流程图
```mermaid
flowchart TD
A[来自 Wcf 的 WxMsg] --> B[Robot.processMsg]
B --> C[MessageSummary 记录消息]
B --> D[_select_model_for_message]
B --> E[_get_specific_history_limit]
B --> F[preprocess → MessageContext]
F --> G{FunctionCallRouter.dispatch}
G -->|直接匹配| H[_check_scope & require_at]
H --> I[_extract_arguments]
I --> J[_invoke_function]
J --> K{FunctionResult.handled?}
K -->|是| L[FunctionResult.dispatch → ctx.send_text]
K -->|否| M[升级到 LLM]
G -->|无直接匹配| M
M --> N[FunctionCallLLM.run]
N --> O{模型是否支持 call_with_functions?}
O -->|是| P[原生工具循环]
P --> Q[调用 call_with_functions]
Q --> R{是否返回 tool call?}
R -->|是| S[_invoke_function → FunctionResult]
S --> T[formatter → 追加工具 JSON]
T --> P
R -->|否| U[模型最终回答]
U --> V[ctx.send_text 最终回复]
P --> W[达到最大轮次]
W --> X[handled = False]
O -->|否| Y[提示词回退]
Y --> Z[get_answer + 解析 JSON]
Z --> AA{action_type == "function"?}
AA -->|是| S
AA -->|否| X
X --> AB[路由返回 False]
AB --> AC[回退:闲聊/帮助路径]
```
## 分步说明
### 1. 消息接入(`robot.py:190-274`
- 每条消息都会通过 `MessageSummary.process_message_from_wxmsg` 持久化,方便后续上下文检索。
- 机器人依据群聊/私聊映射与历史限制选择 AI 模型,再构建包含发送者信息与清洗文本的 `MessageContext`
- 在进入路由前Context 会注入当前 `chat` 模型及会话级的历史上限。
### 2. 函数注册(`function_calls/init_handlers.py:1-9`
- 机器人启动时导入 `function_calls.init_handlers`,所有 `@tool_function` 装饰器即被执行。
- 每个处理器声明名称、描述、JSON Schema、作用域以及是否需要 @,注册表因此具备自描述能力。
### 3. 直接命令快路(`function_calls/router.py:81-175`
- 路由器先对 `ctx.text` 归一化,再走 `_try_direct_command_match` 匹配已知关键词。
- 作用域、@ 以及(待实现的)权限检查会阻止不符合条件的调用。参数会按 JSON Schema 校验,避免脏数据。
- 命中后处理器立即执行,`FunctionResult.dispatch` 会直接向聊天对象推送,无需经过模型。
### 4. LLM 编排(`function_calls/llm.py:33-186`
- 若无直接命中,`FunctionCallLLM.run` 会判断当前模型是否支持 OpenAI 风格的工具调用。
- **原生循环**:协调器不断发送最新对话,执行指定工具,并把结构化 JSON 响应回灌给模型,直到拿到最终回复或达到轮次上限。
- **提示词回退**:不支持原生工具的模型会收到列出所有函数的 system prompt必须返回 JSON 决策供路由器执行。
- 两种路径最终都返回 `LLMRunResult`,路由器据此决定是否直接回复或继续走其他兜底逻辑。
### 5. 处理器执行(`function_calls/handlers.py:37-180`
- 处理器依赖 `function_calls/services` 中的业务封装,统一返回 `FunctionResult`
- 群聊场景会在 `at` 字段写入 `ctx.msg.sender`,确保回复时点名原始请求者。
### 6. 兜底逻辑(`robot.py:229-273`
- 当路由返回未处理状态时,机器人会回退到旧流程:自动通过好友请求、发送欢迎消息或调用 `run_chat_fallback`
- 即使 Function Call 路由失败,整体对话体验依旧有保障。
## 优势
- 所有能力通过单一的 Function Call 路由注册与执行,避免了正则命令与 AI 决策两套体系并存的问题。
- LLM 协调器完全依赖模型原生的函数调用能力,逻辑集中在 `_run_native_loop``function_calls/llm.py:57-134`)。
- `FunctionResult` 既可直接回复,也能作为工具输出反馈给模型,减少重复实现(`function_calls/spec.py:12-36`)。
## 仍需关注的点
- 进入 LLM 流程后,工具输出依赖模型二次组织文本;关键函数可考虑直接派发 `FunctionResult`,避免模型返回空字符串时用户无感知(`function_calls/llm.py:83-134`)。
- 权限检查字段(`spec.auth`)仍是占位符,新增高权限工具前需补齐校验实现(`function_calls/router.py:35-38`)。
---
为快速理解全新的 Function Call Agent 流程而生成。