跳到主要内容

工具调用的 HTTP 请求响应流程

本文档展示用户发送"读取 README.md 文件"时的完整工具调用流程。

1. 前端发送请求

HTTP 请求

POST /api/chat/send HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"message": "读取 README.md 文件",
"attachments": null
}

2. 后端构建请求

2.1 构建 messages 数组(第一次调用 LLM)

[
{
"role": "system",
"content": "# 核心身份\n\n你的名字是\"小C\"...(完整系统提示词)"
},
{
"role": "user",
"content": "读取 README.md 文件"
}
]

2.2 工具定义数组

发送给 LLM 的 tools 参数(完整示例):

[
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read file contents with line numbers. Supports single file or batch mode (multiple files in one call). Line ranges: start_line/end_line (1-based, inclusive). Examples:\n- Single: read_file(path='a.py')\n- Range: read_file(path='a.py', start_line=10, end_line=20)\n- Batch: read_file(paths=['a.py', 'b.py', 'skills/weather/SKILL.md'])\nBatch mode is more efficient for multiple files (saves tool calls).",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Path to a single file (relative to workspace or absolute). Use this OR 'paths', not both."
},
"paths": {
"type": "array",
"items": {"type": "string"},
"description": "List of file paths for batch reading (more efficient than multiple calls). Use this OR 'path', not both."
},
"start_line": {
"type": "integer",
"description": "Start line number (1-based, inclusive). Only works in single file mode. Omit to start from beginning."
},
"end_line": {
"type": "integer",
"description": "End line number (1-based, inclusive). Only works in single file mode. Omit to read to end."
},
"show_line_numbers": {
"type": "boolean",
"description": "Show line numbers in output (default: true)"
}
},
"oneOf": [
{"required": ["path"]},
{"required": ["paths"]}
]
}
}
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Write content to a file. Two modes:\n- mode='overwrite' (default): create or overwrite the file\n- mode='append': append to end of file (creates if not exists)\nIMPORTANT: For large content (>2000 chars), you MUST split into multiple calls:\n 1. write_file(path='a.html', content='<first part>') → create\n 2. write_file(path='a.html', content='<second part>', mode='append')\n 3. write_file(path='a.html', content='<third part>', mode='append')\nThis prevents token limit truncation errors.",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Path to the file (relative to workspace or absolute)"
},
"content": {
"type": "string",
"description": "Content to write"
},
"mode": {
"type": "string",
"enum": ["overwrite", "append"],
"description": "Write mode: 'overwrite' (default) or 'append'"
}
},
"required": ["path", "content"]
}
}
},
{
"type": "function",
"function": {
"name": "exec",
"description": "Execute shell command...",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string"},
"timeout": {"type": "integer"}
},
"required": ["command"]
}
}
}
// ... 其他工具定义(memory, web_fetch 等)
]

3. LLM 返回工具调用

3.1 LLM 响应(包含 tool_calls)

{
"id": "chatcmpl-456",
"object": "chat.completion.chunk",
"created": 1705315900,
"model": "gpt-4",
"choices": [
{
"index": 0,
"delta": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"index": 0,
"id": "call_abc123",
"type": "function",
"function": {
"name": "read_file",
"arguments": "{\"path\":\"README.md\",\"show_line_numbers\":true}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

3.2 后端解析 tool_calls

# backend/modules/agent/loop.py - process_message()
tool_calls_buffer = []

async for chunk in self.provider.chat_stream(...):
if chunk.is_tool_call and chunk.tool_call:
tool_calls_buffer.append(chunk.tool_call)

解析后的 tool_calls_buffer

[
ToolCall(
id="call_abc123",
name="read_file",
arguments={"path": "README.md", "show_line_numbers": True}
)
]

4. 后端执行工具

4.1 发送 WebSocket 通知(工具开始)

# backend/ws/tool_notifications.py
await notify_tool_execution(
session_id=session_id,
tool_name="read_file",
arguments={"path": "README.md", "show_line_numbers": True},
)

WebSocket 消息:

{
"type": "tool_start",
"tool": "read_file",
"arguments": {
"path": "README.md",
"show_line_numbers": true
},
"timestamp": 1705315900.123
}

4.2 执行工具

# backend/modules/tools/registry.py - execute()
result = await self.tools.execute(
tool_name="read_file",
arguments={"path": "README.md", "show_line_numbers": True}
)

工具执行结果:

[File: README.md | Lines: 150]
1| # CountBot
2|
3| CountBot 是一个基于 LLM 的智能助手框架。
4|
5| ## 功能特性
6|
7| - 多模型支持(OpenAI、Claude、国产大模型)
8| - 工具调用系统
9| - 记忆管理
10| - 技能系统
11| - 多渠道接入(Web、钉钉、Telegram)
12|
13| ## 快速开始
14|
15| ```bash
16| pip install -r requirements.txt
17| python backend/app.py
18| ```
19|
20| ## 配置
21|
22| 编辑 `config.yaml` 文件...
... (省略后续内容)

4.3 记录工具调用到数据库

# backend/modules/tools/conversation_history.py
conversation_history.add_conversation(
session_id=session_id,
tool_name="read_file",
arguments={"path": "README.md", "show_line_numbers": True},
user_message="读取 README.md 文件",
result="[File: README.md | Lines: 150]\n 1| # CountBot...",
duration_ms=45
)

数据库 tool_conversations 表记录:

{
"id": "tc_789xyz",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"tool_name": "read_file",
"arguments": "{\"path\":\"README.md\",\"show_line_numbers\":true}",
"result": "[File: README.md | Lines: 150]\n 1| # CountBot...",
"error": null,
"duration_ms": 45,
"timestamp": "2024-01-15T10:30:10Z",
"user_message": "读取 README.md 文件",
"message_id": 3
}

4.4 发送 WebSocket 通知(工具完成)

await notify_tool_execution(
session_id=session_id,
tool_name="read_file",
arguments={"path": "README.md", "show_line_numbers": True},
result="[File: README.md | Lines: 150]\n 1| # CountBot..."
)

WebSocket 消息:

{
"type": "tool_complete",
"tool": "read_file",
"result": "[File: README.md | Lines: 150]\n 1| # CountBot...",
"duration_ms": 45
}

5. 将工具结果添加到 messages

5.1 添加 assistant 消息(包含 tool_calls)

# backend/modules/agent/context.py - add_assistant_message()
messages.append({
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "read_file",
"arguments": "{\"path\":\"README.md\",\"show_line_numbers\":true}"
}
}
]
})

5.2 添加 tool 消息(工具结果)

# backend/modules/agent/context.py - add_tool_result()
messages.append({
"role": "tool",
"tool_call_id": "call_abc123",
"name": "read_file",
"content": "[File: README.md | Lines: 150]\n 1| # CountBot..."
})

5.3 完整的 messages 数组(第二次调用 LLM)

[
{
"role": "system",
"content": "# 核心身份\n\n你的名字是\"小C\"...(完整系统提示词)"
},
{
"role": "user",
"content": "读取 README.md 文件"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "read_file",
"arguments": "{\"path\":\"README.md\",\"show_line_numbers\":true}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"name": "read_file",
"content": "[File: README.md | Lines: 150]\n 1| # CountBot\n 2| \n 3| CountBot 是一个基于 LLM 的智能助手框架..."
}
]

6. 第二次调用 LLM

6.1 发送请求

{
"model": "gpt-4",
"messages": [
{
"role": "system",
"content": "# 核心身份..."
},
{
"role": "user",
"content": "读取 README.md 文件"
},
{
"role": "assistant",
"content": null,
"tool_calls": [...]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"name": "read_file",
"content": "[File: README.md | Lines: 150]..."
}
],
"tools": [...],
"temperature": 0.7,
"max_tokens": 4096,
"stream": true
}

6.2 LLM 返回最终响应

data: {"choices":[{"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"我已经"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"读取了"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" README.md "},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"文件"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"。这是"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" CountBot "},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"项目的"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"说明文档"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":",主要"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"介绍了"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"以下"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"内容"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":":\n\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"1. "},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"**功能特性**"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":":\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" - 多模型支持"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"(OpenAI、Claude、"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":"国产大模型)\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" - 工具调用系统\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" - 记忆管理\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" - 技能系统\n"},"finish_reason":null}]}

data: {"choices":[{"delta":{"content":" - 多渠道接入"},"finish_reason":null}]}

data: {"choices":[{"delta":{},"finish_reason":"stop"}]}

data: [DONE]

7. 返回给前端

7.1 SSE 事件流

event: start
data: {"message_id": "3"}

event: message
data: {"content": "我已经"}

event: message
data: {"content": "读取了"}

event: message
data: {"content": " README.md "}

event: message
data: {"content": "文件"}

event: message
data: {"content": "。这是"}

event: message
data: {"content": " CountBot "}

event: message
data: {"content": "项目的"}

event: message
data: {"content": "说明文档"}

event: message
data: {"content": ",主要"}

event: message
data: {"content": "介绍了"}

event: message
data: {"content": "以下"}

event: message
data: {"content": "内容"}

event: message
data: {"content": ":\n\n"}

event: message
data: {"content": "1. "}

event: message
data: {"content": "**功能特性**"}

event: message
data: {"content": ":\n"}

event: message
data: {"content": " - 多模型支持"}

event: message
data: {"content": "(OpenAI、Claude、"}

event: message
data: {"content": "国产大模型)\n"}

event: message
data: {"content": " - 工具调用系统\n"}

event: message
data: {"content": " - 记忆管理\n"}

event: message
data: {"content": " - 技能系统\n"}

event: message
data: {"content": " - 多渠道接入"}

event: done
data: {"message_id": "4"}

8. 查询工具调用历史

8.1 获取消息列表(包含工具调用)

GET /api/chat/sessions/550e8400-e29b-41d4-a716-446655440000/messages HTTP/1.1
Host: localhost:8000

8.2 响应

[
{
"id": 3,
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"role": "user",
"content": "读取 README.md 文件",
"created_at": "2024-01-15T10:30:10Z",
"tool_calls": []
},
{
"id": 4,
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"role": "assistant",
"content": "我已经读取了 README.md 文件。这是 CountBot 项目的说明文档,主要介绍了以下内容:\n\n1. **功能特性**:\n - 多模型支持(OpenAI、Claude、国产大模型)\n - 工具调用系统\n - 记忆管理\n - 技能系统\n - 多渠道接入",
"created_at": "2024-01-15T10:30:15Z",
"tool_calls": [
{
"id": "tc_789xyz",
"name": "read_file",
"arguments": {
"path": "README.md",
"show_line_numbers": true
},
"result": "[File: README.md | Lines: 150]\n 1| # CountBot\n 2| \n 3| CountBot 是一个基于 LLM 的智能助手框架...",
"error": null,
"status": "success",
"duration": 45,
"spawn_task": null
}
]
}
]

完整流程图

用户: "读取 README.md 文件"

前端: POST /api/chat/send

后端: 构建 messages + tools

LLM (第1次): 返回 tool_calls

后端: 执行 read_file 工具
├─ WebSocket: tool_start
├─ 读取文件
├─ 保存到数据库
└─ WebSocket: tool_complete

后端: 添加 tool 结果到 messages

LLM (第2次): 返回最终响应

前端: SSE 流式显示

用户: 看到完整回复

关键字段说明

字段类型说明
tool_callsarrayLLM 返回的工具调用列表
tool_call_idstring工具调用的唯一标识符
function.namestring工具名称
function.argumentsstringJSON 字符串格式的工具参数
finish_reasonstringtool_calls 表示需要调用工具
duration_msinteger工具执行耗时(毫秒)
spawn_taskobject|null子代理任务详情(仅 spawn 工具)