docs: 更新 README 文档,详细说明新增功能

主要更新:
- 新增消息历史转换功能说明 (4.1)
- 更新响应解析器特性说明 (4.3)
- 添加关键特性说明章节 (8)
- 补充带消息历史的 API 请求示例 (7.2)
- 新增更新日志章节 (10)
- 完善测试脚本说明

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Vertex-AI-Step-Builder
2025-12-31 13:35:45 +00:00
parent 5c2904e010
commit 6bcdbc2560

259
README.md
View File

@@ -2,20 +2,20 @@
## 1. 概述 (Overview)
本项目是一个基于 FastAPI 实现的智能LLM大语言模型代理服务。其核心功能是拦截发往LLMAPI请求动态地将客户端定义的`tools`工具信息注入到提示词Prompt然后将LLM返回的结果进行解析将其中可能包含的工具调用Tool Call指令提取出来最后以结构化的格式返回给调用者。
本项目是一个基于 FastAPI 实现的智能 LLM大语言模型代理服务。其核心功能是拦截发往 LLMAPI 请求,动态地将客户端定义的 `tools`工具信息注入到提示词Prompt然后将 LLM 返回的结果进行解析将其中可能包含的工具调用Tool Call指令提取出来最后以结构化的格式返回给调用者。
这使得即使底层LLM原生不支持工具调用参数我们也能通过提示工程的方式赋予其使用工具的能力。
这使得即使底层 LLM 原生不支持工具调用参数,我们也能通过提示工程的方式赋予其使用工具的能力。
## 2. 设计原则 (Design Principles)
本程序在设计上严格遵循了以下原则:
- **高内聚 (High Cohesion)**: 业务逻辑被集中在服务层 (`app/services.py`) 中与API路由和数据模型分离。
- **高内聚 (High Cohesion)**: 业务逻辑被集中在服务层 (`app/services.py`) 中,与 API 路由和数据模型分离。
- **低耦合 (Low Coupling)**:
- API层 (`app/main.py`) 只负责路由和请求校验,不关心业务实现细节。
- API 层 (`app/main.py`) 只负责路由和请求校验,不关心业务实现细节。
- 通过依赖注入 (`Depends`) 获取配置,避免了全局状态。
- LLM调用被抽象为独立的函数方便未来切换不同的LLM后端或在测试中使用模拟Mock实现。
- **可测试性 (Testability)**: 项目包含了完整的单元测试和集成测试 (`tests/`)使用 `pytest``TestClient`确保每个模块的正确性和整体流程的稳定性。
- LLM 调用被抽象为独立的函数,方便未来切换不同的 LLM 后端或在测试中使用模拟Mock实现。
- **可测试性 (Testability)**: 项目包含了完整的单元测试和集成测试 (`tests/`)以及功能测试脚本,确保每个模块的正确性和整体流程的稳定性。
## 3. 项目结构 (Project Structure)
@@ -23,47 +23,110 @@
.
├── app/ # 核心应用代码
│ ├── core/ # 配置管理
│ │ └── config.py
│ │ └── config.py # 环境变量配置
│ ├── main.py # FastAPI 应用实例和 API 路由
│ ├── models.py # Pydantic 数据模型
── services.py # 核心业务逻辑
── services.py # 核心业务逻辑
│ ├── response_parser.py # 响应解析器(工具调用提取)
│ └── database.py # 数据库操作(请求日志)
├── tests/ # 测试代码
── test_main.py
── test_main.py
│ ├── test_services.py
│ └── test_response_parser.py
├── test_*.py # 功能测试脚本
│ ├── test_tool_call_conversion.py # 工具调用转换测试
│ ├── test_multiple_tool_calls.py # 多工具调用测试
│ └── test_content_with_tool_calls.py # 内容和工具调用混合测试
├── .env # 环境变量文件 (需手动创建)
├── .gitignore # Git 忽略文件
├── README.md # 本文档
└── .venv/ # Python 虚拟环境 (由 uv 创建)
└── .venv/ # Python 虚拟环境
```
## 4. 核心逻辑详解 (Core Logic)
### 4.1. 提示词注入 (Prompt Injection)
### 4.1. 消息历史转换 (Message History Conversion)
**新增功能** - 这是本次更新的核心功能之一。
- **实现函数**: `app.services.convert_tool_calls_to_content`
- **策略**:
1. 遍历消息历史,识别 `role``assistant` 且包含 `tool_calls` 的消息。
2. 将这些消息中的 `tool_calls` 转换为 LLM 可理解的 XML 格式 `{"name": "tool_name", "arguments": {...}}`
3. 保留消息原有的 `content` 字段(如果存在)。
4. 支持多个 tool_calls 的转换,用换行符连接。
- **目的**: 确保消息历史中的工具调用能够被底层 LLM 理解,保持对话上下文的连贯性。
**转换示例**:
```python
# 转换前
{
"role": "assistant",
"tool_calls": [
{"function": {"name": "get_weather", "arguments": '{"location": "北京"}'}}
]
}
# 转换后(发送给 LLM
{
"role": "assistant",
"content": "<invoke>{\"name\": \"get_weather\", \"arguments\": {\"location\": \"北京\"}}</invoke>"
}
```
### 4.2. 提示词注入 (Prompt Injection)
- **实现函数**: `app.services.inject_tools_into_prompt`
- **策略**:
1. 将客户端请求中 `tools` 列表JSON数组序列化为格式化的JSON字符串。
1. 将客户端请求中 `tools` 列表JSON数组序列化为格式化的 JSON 字符串。
2. 创建一个新的、`role``system` 的独立消息。
3. 此消息包含明确的指令告诉LLM它拥有哪些工具以及如何通过特定的格式来调用它们。
4. **调用格式约定**: 指示LLM在需要调用工具时必须输出一个 `<tool_call>{...}</tool_call>` 的XML标签其中包含一个带有 `name``arguments` 字段的JSON对象
5. 这个系统消息被插入到原始消息列表的第二个位置索引1然后整个修改后的消息列表被发送到真实的LLM后端
- **目的**: 对调用者透明,将工具使用的契约通过上下文传递给LLM。
3. 此消息包含明确的指令,告诉 LLM 它拥有哪些工具以及如何通过特定的格式来调用它们。
4. **调用格式约定**: 指示 LLM 在需要调用工具时,必须输出一个 `{"name": "tool_name", "arguments": {...}}` 的 XML 标签
5. 这个系统消息被插入到消息列表的开头
- **目的**: 对调用者透明,将工具使用的"契约"通过上下文传递给 LLM。
### 4.2. 响应解析 (Response Parsing)
### 4.3. 响应解析 (Response Parsing)
- **实现函数**: `app.services.parse_llm_response`
- **实现**: `app.response_parser.ResponseParser`
- **策略**:
1. 使用正则表达式 (`re.search`) 在LLM返回的文本响应中查找 `<tool_call>...</tool_call>` 标签。
2. 如果找到它会提取标签内的JSON字符串并将其解析为一个结构化的 `ToolCall` 对象。此时,返回给客户端的 `ResponseMessage``tool_calls` 字段将被填充,而 `content` 字段可能为 `None`
3. 如果未找到标签则将LLM的全部响应视为常规的文本内容,填充 `content` 字段。
- **目的**: 将LLM的非结构化或半结构化输出转换为客户端可以轻松处理的、定义良好的结构化数据
1. 使用**非贪婪正则表达式**在 LLM 返回的文本响应中查找**所有** `...` 标签。
2. 支持同时解析**多个 tool_calls**
3. 提取工具调用前后的文本内容,合并到 `content` 字段。
4. 如果找到工具调用,将 `tool_calls` 字段填充为结构化的 `ToolCall` 对象列表
5. 如果未找到标签,则将 LLM 的全部响应视为常规的文本内容。
- **新特性**:
-**支持多个 tool_calls 同时解析** - 使用非贪婪匹配和 finditer
-**支持 content 和 tool_calls 同时存在** - 符合 OpenAI API 规范
-**支持文本在前、在后或前后都有文本的场景**
- **目的**: 将 LLM 的非结构化输出转换为标准的 OpenAI 格式响应。
**响应示例**:
```json
{
"message": {
"role": "assistant",
"content": "好的,我来帮你查询。",
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"北京\"}"
}
}
]
}
}
```
## 5. 配置管理 (Configuration)
- 配置文件为根目录下的 `.env`
- `app/core/config.py` 中的 `get_settings` 函数通过依赖注入的方式在每次请求时加载环境变量,确保配置的实时性和在测试中的灵活性
- `app/core/config.py` 中的 `get_settings` 函数通过依赖注入的方式在每次请求时加载环境变量。
- **必需变量**:
- `REAL_LLM_API_URL`: 真实LLM后端的地址
- `REAL_LLM_API_KEY`: 用于访问真实LLMAPI密钥
- `REAL_LLM_API_URL`: 真实 LLM 后端的地址
- `REAL_LLM_API_KEY`: 用于访问真实 LLMAPI 密钥
## 6. 如何运行与测试 (Usage)
@@ -71,28 +134,49 @@
```bash
# 创建虚拟环境
uv venv
python -m venv .venv
# 激活虚拟环境
source .venv/bin/activate # Linux/Mac
# 或
.venv\Scripts\activate # Windows
# 安装依赖
uv pip install fastapi uvicorn httpx pytest
pip install fastapi uvicorn httpx pytest python-dotenv
```
### 6.2. 运行开发服务器
### 6.2. 配置环境变量
创建 `.env` 文件:
```bash
REAL_LLM_API_URL="https://api.example.com/v1/chat/completions"
REAL_LLM_API_KEY="your-api-key"
```
### 6.3. 运行开发服务器
```bash
uvicorn app.main:app --reload
```
服务将运行在 `http://127.0.0.1:8000`
### 6.3. 运行测试
### 6.4. 运行测试
```bash
# 使用 .venv 中的 python 解释器执行 pytest
.venv/bin/python -m pytest
# 运行所有单元测试
pytest
# 运行功能测试脚本
python test_tool_call_conversion.py # 测试工具调用转换
python test_multiple_tool_calls.py # 测试多工具调用
python test_content_with_tool_calls.py # 测试内容和工具调用混合
```
## 7. API 端点示例 (API Example)
### 7.1. 基本请求
**端点**: `POST /v1/chat/completions`
**请求示例 (带工具)**:
@@ -101,7 +185,7 @@ curl -X POST "http://127.0.0.1:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "What is the weather in San Francisco?"}
{"role": "user", "content": "What is the weather in Beijing?"}
],
"tools": [
{
@@ -109,16 +193,115 @@ curl -X POST "http://127.0.0.1:8000/v1/chat/completions" \
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {}
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
}
}
}
}
]
}'
```
## 8. 未来升级方向 (Future Improvements)
### 7.2. 带消息历史的请求
- **支持多种LLM后端**: 修改 `call_llm_api_real` 函数使其能根据请求参数或配置选择不同的LLM提供商。
- **更灵活的工具调用格式**: 支持除XML标签外的其他格式例如纯JSON输出模式。
- **流式响应 (Streaming)**: 支持LLM的流式输出并实时解析和返回给客户端。
- **错误处理增强**: 针对不同的LLM API错误码和网络问题提供更精细的错误反馈。
```bash
curl -X POST "http://127.0.0.1:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "What is the weather in Beijing?"},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"Beijing\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_123",
"content": "Temperature: 20°C, Sunny"
},
{"role": "user", "content": "What about Shanghai?"}
],
"tools": [...]
}'
```
注意:消息历史中的 `tool_calls` 会被自动转换为 XML 格式发送给 LLM。
## 8. 关键特性说明 (Key Features)
### 8.1. 多工具调用支持
系统现在支持在单次响应中返回多个工具调用:
```
LLM 输出:
<invoke>{"name": "get_weather", "arguments": {"location": "北京"}}</invoke>
<invoke>{"name": "get_weather", "arguments": {"location": "上海"}}</invoke>
解析为:
{
"tool_calls": [
{"function": {"name": "get_weather", ...}},
{"function": {"name": "get_weather", ...}}
]
}
```
### 8.2. 内容和工具调用混合
支持同时返回文本内容和工具调用:
```
LLM 输出:
好的,我来帮你查询。
<invoke>{"name": "search", "arguments": {"query": "..."}}</invoke>
请稍等片刻。
解析为:
{
"content": "好的,我来帮你查询。 请稍等片刻。",
"tool_calls": [...]
}
```
### 8.3. OpenAI 兼容性
- ✅ 完全兼容 OpenAI Chat Completions API 格式
- ✅ 支持流式和非流式响应
- ✅ 支持工具调用定义和执行
- ⚠️ 注意:虽然 OpenAI 的 GPT-4o 等模型通常只返回 `content``tool_calls` 中的一个,但本代理支持两者同时存在,以提供更大的灵活性并兼容不同的后端 LLM。
## 9. 未来升级方向 (Future Improvements)
- **支持多种 LLM 后端**: 修改调用函数,使其能根据请求参数或配置选择不同的 LLM 提供商。
- **更灵活的工具调用格式**: 支持除 XML 标签外的其他格式,例如纯 JSON 输出模式。
- **错误处理增强**: 针对不同的 LLM API 错误码和网络问题,提供更精细的错误反馈。
- **性能优化**: 添加缓存机制,减少重复请求的处理时间。
- **监控和日志**: 增强日志系统,添加性能监控和告警功能。
## 10. 更新日志 (Changelog)
### v1.1.0 (最新)
- ✨ 新增消息历史转换功能,支持 tool_calls 到 XML 格式的转换
- ✨ 优化响应解析器,支持多个 tool_calls 同时解析
- ✨ 支持内容和工具调用混合返回
- ✨ 添加完整的功能测试覆盖
- 🐝 修复流式工具调用解析的边界情况
### v1.0.0
- 🎉 初始版本
- ✨ 实现基本的工具调用代理功能
- ✨ OpenAI 兼容的 API 接口
- ✨ 流式和非流式响应支持