Files
llmproxy/README.md
Vertex-AI-Step-Builder 6bcdbc2560 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>
2025-12-31 13:35:45 +00:00

308 lines
10 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.

# LLM Tool Proxy
## 1. 概述 (Overview)
本项目是一个基于 FastAPI 实现的智能 LLM大语言模型代理服务。其核心功能是拦截发往 LLM 的 API 请求,动态地将客户端定义的 `tools`工具信息注入到提示词Prompt然后将 LLM 返回的结果进行解析将其中可能包含的工具调用Tool Call指令提取出来最后以结构化的格式返回给调用者。
这使得即使底层 LLM 原生不支持工具调用参数,我们也能通过提示工程的方式赋予其使用工具的能力。
## 2. 设计原则 (Design Principles)
本程序在设计上严格遵循了以下原则:
- **高内聚 (High Cohesion)**: 业务逻辑被集中在服务层 (`app/services.py`) 中,与 API 路由和数据模型分离。
- **低耦合 (Low Coupling)**:
- API 层 (`app/main.py`) 只负责路由和请求校验,不关心业务实现细节。
- 通过依赖注入 (`Depends`) 获取配置,避免了全局状态。
- LLM 调用被抽象为独立的函数,方便未来切换不同的 LLM 后端或在测试中使用模拟Mock实现。
- **可测试性 (Testability)**: 项目包含了完整的单元测试和集成测试 (`tests/`),以及功能测试脚本,确保每个模块的正确性和整体流程的稳定性。
## 3. 项目结构 (Project Structure)
```
.
├── app/ # 核心应用代码
│ ├── core/ # 配置管理
│ │ └── config.py # 环境变量配置
│ ├── main.py # FastAPI 应用实例和 API 路由
│ ├── models.py # Pydantic 数据模型
│ ├── services.py # 核心业务逻辑
│ ├── response_parser.py # 响应解析器(工具调用提取)
│ └── database.py # 数据库操作(请求日志)
├── tests/ # 测试代码
│ ├── 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 虚拟环境
```
## 4. 核心逻辑详解 (Core Logic)
### 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 字符串。
2. 创建一个新的、`role``system` 的独立消息。
3. 此消息包含明确的指令,告诉 LLM 它拥有哪些工具以及如何通过特定的格式来调用它们。
4. **调用格式约定**: 指示 LLM 在需要调用工具时,必须输出一个 `{"name": "tool_name", "arguments": {...}}` 的 XML 标签。
5. 这个系统消息被插入到消息列表的开头。
- **目的**: 对调用者透明,将工具使用的"契约"通过上下文传递给 LLM。
### 4.3. 响应解析 (Response Parsing)
- **实现类**: `app.response_parser.ResponseParser`
- **策略**:
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` 函数通过依赖注入的方式在每次请求时加载环境变量。
- **必需变量**:
- `REAL_LLM_API_URL`: 真实 LLM 后端的地址
- `REAL_LLM_API_KEY`: 用于访问真实 LLM 的 API 密钥
## 6. 如何运行与测试 (Usage)
### 6.1. 环境设置
```bash
# 创建虚拟环境
python -m venv .venv
# 激活虚拟环境
source .venv/bin/activate # Linux/Mac
# 或
.venv\Scripts\activate # Windows
# 安装依赖
pip install fastapi uvicorn httpx pytest python-dotenv
```
### 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.4. 运行测试
```bash
# 运行所有单元测试
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`
**请求示例 (带工具)**:
```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?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
}
}
}
}
]
}'
```
### 7.2. 带消息历史的请求
```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 接口
- ✨ 流式和非流式响应支持