GenerationLLM 生成模型¶
GenerationLLM 是为连续文本生成场景设计的 LLM 抽象类,适合 DSL 生成、代码编辑、图提取等需要连续输出的场景。DeskLLM 是 GenerationLLM 的主要实现,专注于桌面环境模拟。
设计理念¶
为什么需要 GenerationLLM¶
虽然 ChatLLM 已经可以处理大部分场景,但在某些特殊需求下,生成模型更有优势:
- 连续工具调用:生成模型在连续工具调用时效果更好
- 不需要数据结构:只需要不断追加内容,无需考虑复杂的 JSON 结构
- 流式输出:生成模型的输出可以自然地逐步生成
- 专用场景优化:如 DSL 生成、代码编辑、图提取等
- 简化 Prompt 结构:通过单一 Prompt 控制整个生成过程
与 ChatLLM 的区别¶
| 特性 | ChatLLM | GenerationLLM |
|---|---|---|
| 主要用途 | 多轮对话、工具调用 | 连续文本生成 |
| 响应格式 | 支持结构化(JSON/工具调用) | 仅纯文本 |
| API 类型 | Chat Completions API | Completions API |
| 消息结构 | 支持多角色(user/assistant/system/tool) | 简化的输入输出 |
| 典型场景 | QA、代码助手、数据分析 | DSL 生成、代码编辑、图提取 |
| Prompt 系统 | 5 个 Prompt 位置 | 7 个 Prompt 位置 |
DeskLLM 桌面环境模拟¶
DeskLLM 是 GenerationLLM 的主要实现,专门用于模拟桌面环境的智能体。
核心概念¶
DeskLLM 将 LLM 视为一个拥有自己桌面环境的智能体,它可以在这个环境中执行各种任务,如编辑文件、运行代码等。
核心优势: - 统一的接口设计:便于切换不同的底层大语言模型 - 丰富的提示词管理:支持多种提示词模板和 7 层架构 - 灵活的上下文管理:包括系统消息、指令消息、工作环境状态等 - 工具调用支持:集成工具执行和中间结果处理
桌面环境隐喻¶
┌─────────────────────────────────────────┐
│ DeskLLM 桌面环境 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Editor │ │ Terminal │ │
│ │ (代码编辑器) │ │ (命令行) │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ 状态变化: │
│ original_desk → 操作 → current_desk │
└─────────────────────────────────────────┘
Prompt 结构(7 层架构)¶
DeskLLM 使用与 ChatLLM 不同的 Prompt 结构,专门优化了桌面环境模拟场景:
| Prompt 类型 | 说明 | 典型用途 |
|---|---|---|
system_prompt |
系统角色、工具范围、背景信息 | 限定 LLM 角色,提供全局上下文 |
instruction_prompt |
Few-shot 学习示例 | 展示期望的行事风格和规则 |
original_desk_screenshot_prompt |
任务初始时的环境状态 | 记录未编辑时的文件原始内容 |
purpose_prompt |
任务目标和要求 | 用户输入或当前任务描述 |
intermediate_prompt |
中间结果和状态变更 | 记录已执行的操作和状态变化 |
current_desk_screenshot_prompt |
任务进行时的环境状态 | 当前文件内容(随操作变化) |
prefix_prompt |
输出前缀 | 强制 LLM 以特定格式开头 |
Prompt 渲染顺序¶
system_prompt ← 全局角色定义
↓
instruction_prompt ← Few-shot 示例
↓
original_desk_screenshot_prompt ← 初始状态
↓
purpose_prompt ← 当前任务
↓
intermediate_prompt ← 中间步骤
↓
current_desk_screenshot_prompt ← 当前状态
↓
prefix_prompt ← 输出引导
↓
[LLM 生成内容]
使用场景¶
- DSL 生成:生成特定领域语言的代码(如 SQL、GraphQL、Excel 公式)
- 代码编辑:自动编辑代码文件(增量修改而非重写)
- 图提取:从文本中提取图谱结构(RDF、OWL)
- 桌面自动化:控制桌面应用程序(模拟鼠标键盘操作)
快速开始¶
使用 DeskLLM¶
from tfrobot.brain.chain.llms.generation_llms.desk_llm.openai_desk import GPTDesk
from tfrobot.brain.chain.prompt.memo_prompt import MemoPrompt
from tfrobot.schema.message.conversation.message_dto import TextMessage
# 创建 DeskLLM
desk_llm = GPTDesk(name="gpt-4o")
# 配置系统提示
desk_llm.system_prompt = [
MemoPrompt(template="你是一个代码编辑助手,专注于 Python 开发。")
]
# 调用
result = desk_llm.complete(
current_input=TextMessage(content="帮我写一个快速排序函数")
)
print(result.generations[0].text)
代码编辑场景示例¶
from tfrobot.brain.chain.llms.generation_llms.desk_llm.claude_desk import ClaudeDesk
from tfrobot.brain.chain.prompt.memo_prompt import MemoPrompt
desk_llm = ClaudeDesk(name="claude-3-5-sonnet-20241022")
# 配置代码编辑场景的 Prompts
desk_llm.system_prompt = [
MemoPrompt(template="你是一个 Python 代码编辑助手。")
]
# 原始文件内容
original_code = """
def add(a, b):
return a + b
"""
desk_llm.original_desk_screenshot_prompt = [
MemoPrompt(template=f"原始文件内容:\n```python\n{original_code}\n```")
]
# 当前任务
desk_llm.purpose_prompt = [
MemoPrompt(
template="请为 add 函数添加类型注解和文档字符串。"
)
]
# 强制输出格式
desk_llm.prefix_prompt = [
MemoPrompt(template="修改后的代码:\n```python\n")
]
# 生成
result = desk_llm.complete(current_input=TextMessage(content="开始编辑"))
print(result.generations[0].text)
DSL 生成场景示例¶
from tfrobot.brain.chain.llms.generation_llms.desk_llm.gemini_desk import GeminiDesk
desk_llm = GeminiDesk(name="gemini-2.5-flash")
# 配置 DSL 生成场景
desk_llm.system_prompt = [
MemoPrompt(template="你是一个 SQL 专家,擅长根据自然语言描述生成 SQL 查询。")
]
# Few-shot 示例
desk_llm.instruction_prompt = [
MemoPrompt(template="""
示例 1:
输入:查询所有年龄大于 18 的用户
输出:SELECT * FROM users WHERE age > 18;
示例 2:
查询每个部门的平均工资
输出:SELECT department, AVG(salary) FROM employees GROUP BY department;
""")
]
# 当前任务
desk_llm.purpose_prompt = [
MemoPrompt(template="输入:查询销售额排名前 10 的产品")
]
# 强制输出前缀
desk_llm.prefix_prompt = [
MemoPrompt(template="输出:")
]
result = desk_llm.complete(current_input=TextMessage(content="生成 SQL"))
print(result.generations[0].text)
核心参数¶
通用参数¶
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name |
str |
- | 模型名称(如 gpt-4o, claude-3-5-sonnet-20241022) |
temperature |
float |
1.0 | 采样温度,0-2 之间 |
max_tokens |
int |
4096 | 最大生成 tokens 数 |
top_p |
float |
1.0 | 核采样参数 |
stop |
str\|list[str] |
- | 停止词列表 |
stream |
bool |
False | 是否流式输出 |
Prompt 参数¶
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
system_prompt |
list[BasePrompt] |
[] |
系统提示(角色定义) |
instruction_prompt |
list[BasePrompt] |
[] |
指令提示(Few-shot 示例) |
original_desk_screenshot_prompt |
list[BasePrompt] |
[] |
初始环境状态 |
purpose_prompt |
list[BasePrompt] |
[UserInputPrompt(...)] |
任务目的提示 |
intermediate_prompt |
list[BasePrompt] |
[] |
中间结果提示 |
current_desk_screenshot_prompt |
list[BasePrompt] |
[] |
当前环境状态 |
prefix_prompt |
list[BasePrompt] |
[] |
输出前缀提示 |
提供商特定参数¶
不同提供商有额外的配置参数,详见各提供商文档。
支持的实现¶
| 类名 | 提供商 | 说明 | 默认模型 |
|---|---|---|---|
GPTDesk |
OpenAI | GPT 系列模型 | gpt-4-turbo-preview |
ClaudeDesk |
Anthropic | Claude 系列模型 | claude-3-opus-20240229 |
GeminiDesk |
Gemini 系列模型 | gemini-2.5-flash |
|
DeepSeekDesk |
DeepSeek | DeepSeek 系列模型 | deepseek-coder |
OllamaDesk |
Ollama | 本地模型 | codellama:7b |
SGLangDesk |
SGLang | SGLang 本地服务 | - |
TencentDSDesk |
腾讯 | 腾讯混元系列 | - |
重要约束¶
仅支持纯文本返回¶
DeskLLM 不支持结构化响应格式(JSON/JSON Schema),只能返回纯文本:
# ❌ 错误:不支持 JSON 格式
desk_llm = GPTDesk(
name="gpt-4o",
response_format={"type": "json_object"} # 会抛出 ValueError
)
# ✅ 正确:使用纯文本,自行解析
desk_llm = GPTDesk(name="gpt-4o")
# 在 Prompt 中提示模型返回 JSON
desk_llm.system_prompt = [
MemoPrompt(template="请以 JSON 格式返回结果。")
]
result = desk_llm.complete(...)
# 手动解析 result.generations[0].text
import json
json_data = json.loads(result.generations[0].text)
代理配置¶
部分 DeskLLM 实现支持代理配置:
# OpenAI/Claude/Gemini 支持 HTTP(S) 代理
desk_llm = GPTDesk(
name="gpt-4o",
proxy_host="127.0.0.1",
proxy_port=7890,
proxy_user=None, # 可选
proxy_pass=None, # 可选
)
# Gemini 还支持 SOCKS5 代理
from tfrobot.brain.chain.llms.generation_llms.desk_llm.gemini_desk import GeminiDesk
desk_llm = GeminiDesk(
name="gemini-2.5-flash",
socks5_proxy=True, # 使用 SOCKS5
proxy_host="127.0.0.1",
proxy_port=1080,
)
API 映射¶
DeskLLM 使用各提供商的 Completions API 而非 Chat Completions API:
| 提供商 | API 类型 | 端点 |
|---|---|---|
| OpenAI | Completions | POST /v1/completions |
| Anthropic | Completions | POST /v1/messages (legacy) |
| Gemini | Generate Content | POST /v1beta/models/{model}:generateContent |
最佳实践¶
1. Prompt 顺序设计¶
合理设计 Prompt 的顺序和内容:
desk_llm = GPTDesk(name="gpt-4o")
# 1. 定义全局角色
desk_llm.system_prompt = [
MemoPrompt(template="你是一个专业的 Python 开发者。")
]
# 2. 提供示例
desk_llm.instruction_prompt = [
MemoPrompt(template="""
示例:
输入:创建一个Person类
输出:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
""")
]
# 3. 提供初始状态
desk_llm.original_desk_screenshot_prompt = [
MemoPrompt(template="# 当前文件为空\n")
]
# 4. 定义任务
desk_llm.purpose_prompt = [
MemoPrompt(template="请创建一个Student类,继承Person类。")
]
# 5. 提供中间步骤(如果有)
desk_llm.intermediate_prompt = []
# 6. 提供当前状态
desk_llm.current_desk_screenshot_prompt = []
# 7. 强制输出格式
desk_llm.prefix_prompt = [
MemoPrompt(template="```python\n")
]
2. 状态管理¶
在多轮生成中正确管理环境状态:
# 初始状态
original_content = read_file("main.py")
desk_llm.original_desk_screenshot_prompt = [
MemoPrompt(template=f"原始文件内容:\n{original_content}")
]
# 第一次修改
desk_llm.purpose_prompt = [
MemoPrompt(template="添加错误处理")
]
result1 = desk_llm.complete(...)
current_content = result1.generations[0].text
# 第二次修改(基于第一次的结果)
desk_llm.current_desk_screenshot_prompt = [
MemoPrompt(template=f"当前文件内容:\n{current_content}")
]
desk_llm.intermediate_prompt = [
MemoPrompt(template="已添加错误处理。")
]
desk_llm.purpose_prompt = [
MemoPrompt(template="添加类型注解")
]
result2 = desk_llm.complete(...)
3. 多模态支持¶
部分 DeskLLM 实现支持多模态输入:
from tfrobot.brain.chain.llms.generation_llms.desk_llm.gemini_desk import GeminiDesk
from tfrobot.schema.message.conversation.message_dto import MultiPartMessage
from tfrobot.schema.message.msg_part import TextPart, ImagePart
desk_llm = GeminiDesk(name="gemini-2.5-flash")
# 多模态输入
msg = MultiPartMessage(content=[
TextPart(text="请描述这个界面的布局"),
ImagePart(image_url=ImgUrl(url="screenshot.png")),
])
result = desk_llm.complete(current_input=msg)
错误处理¶
上下文超长¶
当上下文超出限制时,抛出 ContextTooLargeError:
from tfrobot.schema.exceptions import ContextTooLargeError
try:
result = desk_llm.complete(current_input=long_input)
except ContextTooLargeError as e:
print(f"当前大小: {e.current_size}")
print(f"目标大小: {e.target_size}")
print(f"模型: {e.model_name}")
# 由 Chain 层自动处理压缩
参数验证¶
# ❌ 错误:尝试设置 JSON 格式
try:
desk_llm = GPTDesk(
name="gpt-4o",
response_format={"type": "json_object"}
)
except ValueError as e:
print(e) # "DeskLLM仅支持纯文本返回..."
性能优化¶
1. 使用流式输出¶
desk_llm = GPTDesk(name="gpt-4o", stream=True)
result = desk_llm.complete(current_input=user_input)
# 内容会逐步生成
2. 控制 max_tokens¶
# 对于简单任务,限制输出长度
desk_llm = GPTDesk(
name="gpt-4o",
max_tokens=512 # 减少延迟和成本
)
3. 使用合适的模型¶
# 简单任务使用小模型
desk_llm = GPTDesk(name="gpt-4o-mini")
# 复杂任务使用大模型
desk_llm = GPTDesk(name="gpt-4o")
架构设计¶
类继承结构¶
DeskLLM 采用了抽象基类 + 具体实现的设计模式:
GenerationLLM (抽象基类)
└── DeskLLM (抽象基类)
├── GPTDesk (OpenAI 实现)
├── GPTGenDesk (OpenAI Completions API)
├── ClaudeDesk (Anthropic 实现)
├── GeminiDesk (Google 实现)
├── DeepSeekDesk (DeepSeek 实现)
├── OllamaDesk (Ollama 本地实现)
├── SGLangDesk (SGLang 本地实现)
└── TencentDSDesk (腾讯混元实现)
每个具体实现类负责与特定的 LLM 服务提供商 API 进行交互,而共享相同的接口和基础功能。
请求处理流程¶
DeskLLM 的标准请求处理流程:
1. 构造请求参数 (construct_request_params)
├─ 收集 7 层 Prompt 内容
├─ 构造消息列表
└─ 设置参数(temperature, max_tokens, etc.)
2. 发送请求到 LLM API (complete/async_complete)
├─ 调用提供商 API
├─ 处理流式/非流式响应
└─ 错误处理和重试
3. 处理响应并构造结果 (construct_llm_result)
├─ 提取生成文本
├─ 统计 token 使用量
└─ 返回 LLMResult 对象
模板系统¶
DeskLLM 支持多种提示词模板系统:
| 模板类型 | 推荐度 | 特点 |
|---|---|---|
| Jinja2PromptTemplate | ✅ 推荐 | 支持条件语句、循环、宏定义、多语言 |
| FStrPromptTemplate | ⚠️ 备选 | 简单格式化,功能有限 |
| StringTemplate | ⚠️ 备选 | 静态字符串 |
推荐使用 Jinja2PromptTemplate:
from tfrobot.brain.chain.prompt.template.jinja2_template import Jinja2PromptTemplate
from tfrobot.schema.types import Locale
template = Jinja2PromptTemplate(
templates={
Locale.EN: "You are a {{ role }}. Your task is to {{ task }}.",
Locale.ZH: "你是一个{{ role }}。你的任务是{{ task }}。"
}
)
实现原则¶
基本原则¶
- 接口一致性:所有 DeskLLM 实现类应保持相同的接口,便于切换不同的底层模型
- 错误处理:实现应包含完善的错误处理机制,特别是网络错误和 API 限制错误
- 上下文管理:合理管理上下文长度,避免超出模型的最大上下文窗口
- 参数验证:使用 Pydantic 进行参数验证,确保参数的正确性
- 异步支持:同时提供同步和异步接口,满足不同场景的需求
注意事项¶
| 注意事项 | 说明 |
|---|---|
| 仅支持纯文本 | DeskLLM 不支持 JSON Schema 格式,只能返回纯文本 |
| 上下文窗口管理 | 当上下文超出限制时,应有策略进行上下文压缩 |
| 代理配置 | 在网络受限环境中,需要正确配置代理 |
| API 密钥管理 | 安全存储和使用 API 密钥,优先使用环境变量 |
| 重试机制 | 对于可重试的错误(如网络超时),应实现适当的重试策略 |
扩展 DeskLLM¶
创建新的 DeskLLM 实现¶
要为新的 LLM 服务提供商创建 DeskLLM 实现,需要:
1. 继承 DeskLLM 抽象基类
from tfrobot.brain.chain.llms.generation_llms.desk_llm.base import DeskLLM
class NewProviderDesk(DeskLLM):
"""新提供商的 DeskLLM 实现"""
pass
2. 实现必要的方法:
- model_post_init:初始化客户端
- construct_request_params:构造请求参数
- complete:同步请求处理
- async_complete:异步请求处理
- construct_llm_result:构造结果对象
3. 参考现有实现:
- OpenAI: openai_desk.py, openai_gen_desk.py
- Anthropic: claude_desk.py
- Google: gemini_desk.py
- DeepSeek: deepseek_desk.py
自定义提示词模板¶
可以通过继承 BasePrompt 和使用 Jinja2PromptTemplate 创建自定义提示词模板:
from tfrobot.brain.chain.prompt.base import BasePrompt
from tfrobot.brain.chain.prompt.template.jinja2_template import Jinja2PromptTemplate
from tfrobot.schema.meta.tf_field import TFField
from tfrobot.schema.types import Locale
class CustomPrompt(BasePrompt):
"""自定义提示词模板"""
template = TFField(
default_factory=lambda: Jinja2PromptTemplate(
templates={
Locale.EN: "Custom template: {{ variable }}",
Locale.ZH: "自定义模板: {{ variable }}"
}
)
)
故障排除¶
常见问题¶
| 问题 | 症状 | 解决方案 |
|---|---|---|
| 上下文长度超限 | API 返回 "context_length_exceeded" 错误 | 减少提示词长度,或使用 collapse_context 方法压缩上下文 |
| API 认证失败 | API 返回 401 错误 | 检查 API 密钥是否正确,是否过期 |
| 网络超时 | 请求超时错误 | 增加 timeout 参数,或配置代理 |
| 参数错误 | 创建时抛出 ValueError | 检查是否设置了不支持的参数(如 response_format) |
调试技巧¶
-
查看实际发送的 Prompt
result = desk_llm.complete(current_input=user_input) print(result.meta_info.get("prompt", "")) -
检查 Token 使用量
print(f"输入: {result.llm_output.tokens_input}") print(f"输出: {result.llm_output.tokens_output}") -
启用详细日志
import logging logging.basicConfig(level=logging.DEBUG) -
使用异常处理获取详细信息
from tfrobot.schema.exceptions import ContextTooLargeError try: result = desk_llm.complete(current_input=long_input) except ContextTooLargeError as e: print(f"当前大小: {e.current_size}") print(f"目标大小: {e.target_size}") print(f"模型: {e.model_name}")
最佳实践总结¶
模型选择¶
根据任务复杂度和性能要求选择合适的模型:
| 任务类型 | 推荐模型 | 理由 |
|---|---|---|
| 简单任务 | GPT-4o-mini, Gemini 2.0 Flash | 成本低、速度快 |
| 复杂推理 | GPT-4o, Claude Opus, DeepSeek-Reasoner | 推理能力强 |
| 中文场景 | DeepSeek-Chat, GLM-4.7 | 中文优化 |
| 代码生成 | GPT-4o, Claude Opus, GLM-4.7 | 代码能力强 |
| 多模态 | GPT-4o, Gemini 3 Pro | 多模态支持完善 |
提示词设计¶
- 使用 Jinja2 模板:优先使用 Jinja2PromptTemplate
- 分层提示词:合理利用 7 层 Prompt,清晰分离不同类型的信息
- 保持简洁:避免冗余信息,减少 token 消耗
- 结构化信息:对于复杂信息,使用结构化格式(如表格、代码块)
- Few-shot 学习:在
instruction_prompt中提供示例
状态管理¶
- 初始状态:使用
original_desk_screenshot_prompt记录 - 中间步骤:使用
intermediate_prompt记录已执行的操作 - 当前状态:使用
current_desk_screenshot_prompt记录当前状态 - 状态同步:确保 Prompt 中的状态与实际文件内容一致
相关文档¶
基础文档¶
DeskLLM 实现文档¶
云端 API 实现(推荐用于生产环境)¶
- GeminiDesk - Google Gemini 实现
- ✅ 多模态任务首选 - 1M 上下文,完善的多模态支持
- ✅ 国内访问相对稳定
-
✅ SOCKS5 代理支持
-
DeepSeekDesk - DeepSeek 实现
- ✅ 纯文本任务推荐 - 中文能力强,价格低
- ✅ FIM(Fill-in-the-Middle)支持
-
✅ 测试充分,稳定性好
-
TencentDSDesk - 腾讯混元实现
- ✅ 国内访问稳定
-
✅ 功能完善,流式输出支持
-
GPTDesk - OpenAI Responses API 实现
- ⚠️ Completion API 支持有限
-
⚠️ 国内访问困难
-
GPTGenDesk - OpenAI Completions API 实现
- ✅ 支持停止词(最多 4 个)
-
⚠️ 仅支持
gpt-3.5-turbo-instruct模型 -
ClaudeDesk - Anthropic Claude 实现
- ⚠️ 国内访问困难
- ⚠️ 未经过充分测试
- 💡 建议使用 GeminiDesk 或 DeepSeekDesk 替代
本地模型实现(仅用于测试)¶
- OllamaDesk - Ollama 本地模型实现
- ⚠️ 仅适用于本地测试,不建议用于生产环境
- ⚠️ 错误处理、测试覆盖均有不足
-
💡 生产环境推荐使用云端 API
-
SGLangDesk - SGLang 本地服务实现
- ⚠️ 仅适用于本地测试
- ⚠️ 需要手动部署和维护