工具调用的 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..."
})