refactor: replace OPRO with simple iterative refinement
Major changes: - Remove fake OPRO evaluation (no more fake 0.5 scores) - Add simple refinement based on user selection - New endpoint: POST /opro/refine (selected + rejected instructions) - Update prompt generation to focus on comprehensive coverage instead of style variety - All generated instructions now start with role definition (你是一个...) - Update README to reflect new approach and API endpoints Technical details: - Added refine_based_on_selection() in prompt_utils.py - Added refine_instruction_candidates() in user_prompt_optimizer.py - Added OPRORefineReq model and /opro/refine endpoint in api.py - Updated frontend handleContinueOptimize() to use new refinement flow - Changed prompt requirements from 'different styles' to 'comprehensive coverage' - Added role definition requirement as first item in all prompt templates
This commit is contained in:
146
README.md
146
README.md
@@ -1,58 +1,65 @@
|
|||||||
# OPRO Prompt Optimizer
|
# System Prompt Generator
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
|
|
||||||
OPRO (Optimization by PROmpting) 是一个基于大语言模型的提示词优化系统。本项目实现了真正的 OPRO 算法,通过迭代优化系统指令(System Instructions)来提升 LLM 在特定任务上的性能。
|
这是一个基于大语言模型的系统提示词(System Prompt)生成和迭代优化工具。通过简单的任务描述,自动生成高质量的系统指令,并支持基于用户选择的迭代改进。
|
||||||
|
|
||||||
### 核心功能
|
### 核心功能
|
||||||
|
|
||||||
- **系统指令优化**:使用 LLM 作为优化器,基于历史性能轨迹生成更优的系统指令
|
- **智能指令生成**:根据任务描述自动生成多个高质量的系统指令候选
|
||||||
- **多轮迭代优化**:支持多轮优化,每轮基于前一轮的性能反馈生成新的候选指令
|
- **迭代式改进**:基于用户选择的指令生成改进版本,避免被拒绝的方向
|
||||||
|
- **角色定义格式**:所有生成的指令都以角色定义开头(如"你是一个..."),符合最佳实践
|
||||||
- **智能候选选择**:通过语义聚类和多样性选择,从大量候选中筛选出最具代表性的指令
|
- **智能候选选择**:通过语义聚类和多样性选择,从大量候选中筛选出最具代表性的指令
|
||||||
- **性能评估**:支持自定义测试用例对系统指令进行自动评估
|
- **会话管理**:支持多个任务的并行管理和历史记录
|
||||||
- **会话管理**:支持多个优化任务的并行管理和历史记录
|
- **全面覆盖要求**:生成的指令全面覆盖任务的所有要求和细节,而非仅追求风格多样性
|
||||||
|
|
||||||
### 用户界面
|
### 用户界面
|
||||||
|
|
||||||
- **现代化聊天界面**:类似 Google Gemini 的简洁设计
|
- **现代化聊天界面**:类似 Google Gemini 的简洁设计
|
||||||
- **侧边栏会话管理**:可折叠的侧边栏,支持多会话切换
|
- **侧边栏会话管理**:可折叠的侧边栏,支持多会话切换
|
||||||
- **实时优化反馈**:每轮优化生成 3-5 个候选指令,用户可选择继续优化或执行
|
- **实时生成反馈**:每轮生成 5 个候选指令,用户可选择继续优化或复制使用
|
||||||
- **模型选择**:支持在界面中选择不同的 LLM 模型
|
- **模型选择**:支持在界面中选择不同的 LLM 模型
|
||||||
|
|
||||||
## 主要优化改进
|
## 核心特性
|
||||||
|
|
||||||
### 1. 真正的 OPRO 实现
|
### 1. 简单直观的工作流程
|
||||||
|
|
||||||
原始代码实现的是查询重写(Query Rewriting),而非真正的 OPRO。我们添加了完整的 OPRO 功能:
|
不同于复杂的 OPRO 算法(需要测试用例和自动评估),本工具采用简单直观的迭代改进方式:
|
||||||
|
|
||||||
- **系统指令生成**:`generate_system_instruction_candidates()` - 生成多样化的系统指令候选
|
- **初始生成**:输入任务描述 → 生成 5 个全面的系统指令候选
|
||||||
- **性能评估**:`evaluate_system_instruction()` - 基于测试用例评估指令性能
|
- **迭代改进**:选择喜欢的指令 → 生成基于该指令的改进版本,同时避免被拒绝的方向
|
||||||
- **轨迹优化**:基于历史 (instruction, score) 轨迹生成更优指令
|
- **无需评分**:不需要测试用例或性能评分,完全基于用户偏好进行改进
|
||||||
- **元提示工程**:专门设计的元提示用于指导 LLM 生成和优化系统指令
|
|
||||||
|
|
||||||
### 2. 性能优化
|
### 2. 高质量指令生成
|
||||||
|
|
||||||
- **候选池大小优化**:从 20 个候选减少到 10 个,速度提升约 2 倍
|
- **角色定义格式**:所有指令以"你是一个..."开头,符合系统提示词最佳实践
|
||||||
- **智能聚类选择**:使用 AgglomerativeClustering 从候选池中选择最具多样性的 Top-K
|
- **全面覆盖要求**:生成的指令全面覆盖任务的所有要求和细节
|
||||||
|
- **清晰可执行**:指令清晰、具体、可执行,包含必要的行为规范和输出格式
|
||||||
|
- **简体中文**:所有生成的指令使用简体中文
|
||||||
|
|
||||||
|
### 3. 性能优化
|
||||||
|
|
||||||
|
- **候选池大小优化**:生成 10 个候选,通过聚类选择 5 个最具多样性的
|
||||||
|
- **智能聚类选择**:使用 AgglomerativeClustering 从候选池中选择最具代表性的指令
|
||||||
- **嵌入服务回退**:Xinference → Ollama 自动回退机制,确保服务可用性
|
- **嵌入服务回退**:Xinference → Ollama 自动回退机制,确保服务可用性
|
||||||
|
|
||||||
### 3. API 架构改进
|
### 4. API 架构
|
||||||
|
|
||||||
- **新增 OPRO 端点**:
|
- **核心端点**:
|
||||||
- `POST /opro/create` - 创建 OPRO 优化任务
|
- `POST /opro/create` - 创建新任务
|
||||||
- `POST /opro/generate_and_evaluate` - 生成并自动评估候选
|
- `POST /opro/generate_and_evaluate` - 生成初始候选
|
||||||
- `POST /opro/execute` - 执行系统指令
|
- `POST /opro/refine` - 基于用户选择进行迭代改进
|
||||||
- `GET /opro/runs` - 获取所有优化任务
|
- `GET /opro/sessions` - 获取所有会话
|
||||||
- `GET /opro/run/{run_id}` - 获取特定任务详情
|
- `GET /opro/runs` - 获取所有任务
|
||||||
- **会话状态管理**:完整的 OPRO 运行状态跟踪(轨迹、测试用例、迭代次数)
|
- **会话管理**:支持多会话、多任务的并行管理
|
||||||
- **向后兼容**:保留原有查询重写功能,标记为 `opro-legacy`
|
- **向后兼容**:保留原有查询重写功能,标记为 `opro-legacy`
|
||||||
|
|
||||||
### 4. 前端界面重构
|
### 5. 前端界面
|
||||||
|
|
||||||
- **Gemini 风格设计**:简洁的白色/灰色配色,圆角设计,微妙的阴影效果
|
- **Gemini 风格设计**:简洁的白色/灰色配色,圆角设计,微妙的阴影效果
|
||||||
- **可折叠侧边栏**:默认折叠,支持会话列表管理
|
- **可折叠侧边栏**:默认折叠,支持会话列表管理
|
||||||
- **多行输入框**:支持多行文本输入,底部工具栏包含模型选择器
|
- **多行输入框**:支持多行文本输入,底部工具栏包含模型选择器
|
||||||
- **候选指令卡片**:每个候选显示编号、内容、分数,提供"继续优化"、"复制"、"执行"按钮
|
- **候选指令卡片**:每个候选显示编号和内容,提供"继续优化"和"复制"按钮
|
||||||
- **简体中文界面**:所有 UI 文本和生成的指令均使用简体中文
|
- **简体中文界面**:所有 UI 文本和生成的指令均使用简体中文
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@@ -97,19 +104,18 @@ uvicorn _qwen_xinference_demo.api:app --host 0.0.0.0 --port 8010
|
|||||||
|
|
||||||
### 访问界面
|
### 访问界面
|
||||||
|
|
||||||
- **OPRO 优化界面**:http://127.0.0.1:8010/ui/opro.html
|
- **系统指令生成器**:http://127.0.0.1:8010/ui/opro.html
|
||||||
- **传统三栏界面**:http://127.0.0.1:8010/ui/
|
- **传统三栏界面**:http://127.0.0.1:8010/ui/
|
||||||
- **API 文档**:http://127.0.0.1:8010/docs
|
- **API 文档**:http://127.0.0.1:8010/docs
|
||||||
- **OpenAPI JSON**:http://127.0.0.1:8010/openapi.json
|
- **OpenAPI JSON**:http://127.0.0.1:8010/openapi.json
|
||||||
|
|
||||||
### 使用示例
|
### 使用示例
|
||||||
|
|
||||||
1. **创建新会话**:在 OPRO 界面点击"新建会话"或侧边栏的 + 按钮
|
1. **创建新会话**:在界面点击"新建会话"或侧边栏的 + 按钮
|
||||||
2. **输入任务描述**:例如"将中文翻译成英文"
|
2. **输入任务描述**:例如"帮我写一个专业的营销文案生成助手"
|
||||||
3. **查看候选指令**:系统生成 3-5 个优化的系统指令
|
3. **查看候选指令**:系统生成 5 个全面的系统指令,每个都以角色定义开头
|
||||||
4. **继续优化**:点击"继续优化"进行下一轮迭代
|
4. **选择并改进**:点击喜欢的指令上的"继续优化"按钮,生成基于该指令的改进版本
|
||||||
5. **执行指令**:点击"执行此指令"测试指令效果
|
5. **复制使用**:点击"复制"按钮将指令复制到剪贴板,用于你的应用中
|
||||||
6. **复制指令**:点击"复制"按钮将指令复制到剪贴板
|
|
||||||
|
|
||||||
## 配置说明
|
## 配置说明
|
||||||
|
|
||||||
@@ -123,8 +129,8 @@ OLLAMA_HOST = "http://127.0.0.1:11434"
|
|||||||
DEFAULT_CHAT_MODEL = "qwen3:8b"
|
DEFAULT_CHAT_MODEL = "qwen3:8b"
|
||||||
DEFAULT_EMBED_MODEL = "qwen3-embedding:4b"
|
DEFAULT_EMBED_MODEL = "qwen3-embedding:4b"
|
||||||
|
|
||||||
# OPRO 优化参数
|
# 生成参数
|
||||||
GENERATION_POOL_SIZE = 10 # 生成候选池大小
|
GENERATION_POOL_SIZE = 10 # 生成候选池大小(生成10个,聚类选择5个)
|
||||||
TOP_K = 5 # 返回给用户的候选数量
|
TOP_K = 5 # 返回给用户的候选数量
|
||||||
CLUSTER_DISTANCE_THRESHOLD = 0.15 # 聚类距离阈值
|
CLUSTER_DISTANCE_THRESHOLD = 0.15 # 聚类距离阈值
|
||||||
|
|
||||||
@@ -157,11 +163,30 @@ XINFERENCE_EMBED_URL = "http://127.0.0.1:9997/models/bge-base-zh/embed"
|
|||||||
|
|
||||||
## API 端点
|
## API 端点
|
||||||
|
|
||||||
### OPRO 相关(推荐使用)
|
### 会话管理
|
||||||
|
|
||||||
|
- `POST /opro/session/create` - 创建新会话
|
||||||
|
- `GET /opro/sessions` - 获取所有会话
|
||||||
|
- `GET /opro/session/{session_id}` - 获取会话详情
|
||||||
|
|
||||||
|
### 任务管理
|
||||||
|
|
||||||
|
- `POST /opro/create` - 在会话中创建新任务
|
||||||
|
- 请求体:`{"session_id": "xxx", "task_description": "任务描述", "model_name": "qwen3:8b"}`
|
||||||
|
- 返回:`{"run_id": "xxx", "task_description": "...", "iteration": 0}`
|
||||||
|
|
||||||
|
### 指令生成
|
||||||
|
|
||||||
|
- `POST /opro/generate_and_evaluate` - 生成初始候选指令
|
||||||
|
- 请求体:`{"run_id": "xxx", "top_k": 5, "pool_size": 10}`
|
||||||
|
- 返回:`{"candidates": [{"instruction": "...", "score": null}, ...]}`
|
||||||
|
|
||||||
|
- `POST /opro/refine` - 基于用户选择进行迭代改进
|
||||||
|
- 请求体:`{"run_id": "xxx", "selected_instruction": "用户选择的指令", "rejected_instructions": ["被拒绝的指令1", "被拒绝的指令2"]}`
|
||||||
|
- 返回:`{"candidates": [{"instruction": "...", "score": null}, ...], "iteration": 1}`
|
||||||
|
|
||||||
|
### 任务查询
|
||||||
|
|
||||||
- `POST /opro/create` - 创建优化任务
|
|
||||||
- `POST /opro/generate_and_evaluate` - 生成并评估候选
|
|
||||||
- `POST /opro/execute` - 执行系统指令
|
|
||||||
- `GET /opro/runs` - 获取所有任务
|
- `GET /opro/runs` - 获取所有任务
|
||||||
- `GET /opro/run/{run_id}` - 获取任务详情
|
- `GET /opro/run/{run_id}` - 获取任务详情
|
||||||
|
|
||||||
@@ -181,6 +206,37 @@ XINFERENCE_EMBED_URL = "http://127.0.0.1:9997/models/bge-base-zh/embed"
|
|||||||
|
|
||||||
详细 API 文档请访问:http://127.0.0.1:8010/docs
|
详细 API 文档请访问:http://127.0.0.1:8010/docs
|
||||||
|
|
||||||
|
## 工作原理
|
||||||
|
|
||||||
|
### 初始生成流程
|
||||||
|
|
||||||
|
1. 用户输入任务描述(如"帮我写一个专业的营销文案生成助手")
|
||||||
|
2. 系统使用 LLM 生成 10 个候选指令
|
||||||
|
3. 通过语义嵌入和聚类算法选择 5 个最具多样性的候选
|
||||||
|
4. 所有候选都以角色定义开头,全面覆盖任务要求
|
||||||
|
|
||||||
|
### 迭代改进流程
|
||||||
|
|
||||||
|
1. 用户选择喜欢的指令(如候选 #3)
|
||||||
|
2. 系统记录被拒绝的指令(候选 #1, #2, #4, #5)
|
||||||
|
3. 向 LLM 发送改进请求:"基于选中的指令生成改进版本,避免被拒绝指令的方向"
|
||||||
|
4. 生成新的 10 个候选,聚类选择 5 个返回
|
||||||
|
5. 用户可以继续迭代或复制使用
|
||||||
|
|
||||||
|
### 与 OPRO 的区别
|
||||||
|
|
||||||
|
**OPRO(原始算法)**:
|
||||||
|
- 需要测试用例(如数学题的正确答案)
|
||||||
|
- 自动评分(如准确率 0.73, 0.81)
|
||||||
|
- 基于性能轨迹优化
|
||||||
|
- 适用于有明确评估标准的任务
|
||||||
|
|
||||||
|
**本工具(简单迭代改进)**:
|
||||||
|
- 不需要测试用例
|
||||||
|
- 不需要自动评分
|
||||||
|
- 基于用户偏好改进
|
||||||
|
- 适用于任意通用任务
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
### 1. 无法连接 Ollama 服务
|
### 1. 无法连接 Ollama 服务
|
||||||
@@ -198,11 +254,17 @@ ollama serve
|
|||||||
|
|
||||||
### 3. 生成速度慢
|
### 3. 生成速度慢
|
||||||
|
|
||||||
- 调整 `GENERATION_POOL_SIZE` 减少候选数量
|
- 调整 `GENERATION_POOL_SIZE` 减少候选数量(如改为 6,返回 3 个)
|
||||||
- 使用更小的模型(如 `qwen3:4b`)
|
- 使用更小的模型(如 `qwen3:4b`)
|
||||||
- 确保 Ollama 使用 GPU 加速
|
- 确保 Ollama 使用 GPU 加速
|
||||||
|
|
||||||
### 4. 界面显示异常
|
### 4. 生成的指令质量不高
|
||||||
|
|
||||||
|
- 提供更详细的任务描述
|
||||||
|
- 多次迭代改进,选择最好的继续优化
|
||||||
|
- 尝试不同的模型
|
||||||
|
|
||||||
|
### 5. 界面显示异常
|
||||||
|
|
||||||
硬刷新浏览器缓存:
|
硬刷新浏览器缓存:
|
||||||
- **Mac**: `Cmd + Shift + R`
|
- **Mac**: `Cmd + Shift + R`
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ from .opro.session_state import (
|
|||||||
from .opro.user_prompt_optimizer import generate_candidates
|
from .opro.user_prompt_optimizer import generate_candidates
|
||||||
from .opro.user_prompt_optimizer import (
|
from .opro.user_prompt_optimizer import (
|
||||||
generate_system_instruction_candidates,
|
generate_system_instruction_candidates,
|
||||||
evaluate_system_instruction
|
evaluate_system_instruction,
|
||||||
|
refine_instruction_candidates
|
||||||
)
|
)
|
||||||
|
|
||||||
from .opro.ollama_client import call_qwen
|
from .opro.ollama_client import call_qwen
|
||||||
@@ -159,6 +160,15 @@ class OPROExecuteReq(BaseModel):
|
|||||||
model_name: Optional[str] = None
|
model_name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class OPRORefineReq(BaseModel):
|
||||||
|
"""Request to refine based on selected instruction (simple iterative refinement, NOT OPRO)."""
|
||||||
|
run_id: str
|
||||||
|
selected_instruction: str
|
||||||
|
rejected_instructions: List[str]
|
||||||
|
top_k: Optional[int] = None
|
||||||
|
pool_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# LEGACY ENDPOINTS (Query Rewriting - NOT true OPRO)
|
# LEGACY ENDPOINTS (Query Rewriting - NOT true OPRO)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -696,3 +706,44 @@ def opro_execute(req: OPROExecuteReq):
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AppException(500, f"Execution failed: {e}", "EXECUTION_ERROR")
|
raise AppException(500, f"Execution failed: {e}", "EXECUTION_ERROR")
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/opro/refine", tags=["opro-true"])
|
||||||
|
def opro_refine(req: OPRORefineReq):
|
||||||
|
"""
|
||||||
|
Simple iterative refinement based on user selection (NOT OPRO).
|
||||||
|
|
||||||
|
This generates new candidates based on the selected instruction while avoiding rejected ones.
|
||||||
|
No scoring, no trajectory - just straightforward refinement based on user preference.
|
||||||
|
"""
|
||||||
|
run = get_opro_run(req.run_id)
|
||||||
|
if not run:
|
||||||
|
raise AppException(404, "OPRO run not found", "RUN_NOT_FOUND")
|
||||||
|
|
||||||
|
top_k = req.top_k or config.TOP_K
|
||||||
|
pool_size = req.pool_size or config.GENERATION_POOL_SIZE
|
||||||
|
|
||||||
|
try:
|
||||||
|
candidates = refine_instruction_candidates(
|
||||||
|
task_description=run["task_description"],
|
||||||
|
selected_instruction=req.selected_instruction,
|
||||||
|
rejected_instructions=req.rejected_instructions,
|
||||||
|
top_k=top_k,
|
||||||
|
pool_size=pool_size,
|
||||||
|
model_name=run["model_name"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update iteration counter
|
||||||
|
update_opro_iteration(req.run_id, candidates)
|
||||||
|
|
||||||
|
# Get updated run info
|
||||||
|
run = get_opro_run(req.run_id)
|
||||||
|
|
||||||
|
return ok({
|
||||||
|
"run_id": req.run_id,
|
||||||
|
"iteration": run["iteration"],
|
||||||
|
"candidates": [{"instruction": c, "score": None} for c in candidates],
|
||||||
|
"task_description": run["task_description"]
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
raise AppException(500, f"Refinement failed: {e}", "REFINEMENT_ERROR")
|
||||||
|
|||||||
@@ -56,14 +56,15 @@ def generate_initial_system_instruction_candidates(task_description: str, pool_s
|
|||||||
目标任务描述:
|
目标任务描述:
|
||||||
【{task_description}】
|
【{task_description}】
|
||||||
|
|
||||||
请根据以上任务,生成 {pool_size} 条高质量、风格各异的"System Instruction"候选指令。
|
请根据以上任务,生成 {pool_size} 条高质量、全面的"System Instruction"候选指令。
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
1. 每条指令必须有明显不同的风格和侧重点
|
1. 每条指令必须以角色定义开头(例如:"你是一个..."、"你是..."等)
|
||||||
2. 覆盖不同的实现策略(例如:简洁型、详细型、示例型、角色扮演型、步骤型等)
|
2. 每条指令必须全面覆盖任务的所有要求和细节
|
||||||
3. 这些指令应指导LLM的行为和输出格式,以最大化任务性能
|
3. 指令应清晰、具体、可执行,能够有效指导LLM完成任务
|
||||||
4. 每条指令单独成行,不包含编号或额外说明
|
4. 确保指令包含必要的行为规范、输出格式、注意事项等
|
||||||
5. 所有生成的指令必须使用简体中文
|
5. 每条指令单独成行,不包含编号或额外说明
|
||||||
|
6. 所有生成的指令必须使用简体中文
|
||||||
|
|
||||||
生成 {pool_size} 条指令:
|
生成 {pool_size} 条指令:
|
||||||
"""
|
"""
|
||||||
@@ -120,11 +121,68 @@ def generate_optimized_system_instruction(
|
|||||||
然后,生成 {pool_size} 条新的、有潜力超越 {highest_score:.4f} 分的System Instruction。
|
然后,生成 {pool_size} 条新的、有潜力超越 {highest_score:.4f} 分的System Instruction。
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
1. 每条指令必须有明显不同的改进策略
|
1. 每条指令必须以角色定义开头(例如:"你是一个..."、"你是..."等)
|
||||||
2. 结合高分指令的优点,避免低分指令的缺陷
|
2. 每条指令必须全面覆盖任务的所有要求和细节
|
||||||
3. 探索新的优化方向和表达方式
|
3. 结合高分指令的优点,避免低分指令的缺陷
|
||||||
4. 每条指令单独成行,不包含编号或额外说明
|
4. 指令应清晰、具体、可执行,能够有效指导LLM完成任务
|
||||||
5. 所有生成的指令必须使用简体中文
|
5. 每条指令单独成行,不包含编号或额外说明
|
||||||
|
6. 所有生成的指令必须使用简体中文
|
||||||
|
|
||||||
生成 {pool_size} 条优化后的指令:
|
生成 {pool_size} 条优化后的指令:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def refine_based_on_selection(
|
||||||
|
task_description: str,
|
||||||
|
selected_instruction: str,
|
||||||
|
rejected_instructions: List[str],
|
||||||
|
pool_size: int = None
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Simple refinement: Generate variations based on selected instruction while avoiding rejected ones.
|
||||||
|
|
||||||
|
This is NOT OPRO - it's straightforward iterative refinement based on user preference.
|
||||||
|
No scoring, no trajectory, just: "I like this one, give me more like it (but not like those)."
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_description: Description of the task
|
||||||
|
selected_instruction: The instruction the user selected
|
||||||
|
rejected_instructions: The instructions the user didn't select
|
||||||
|
pool_size: Number of new candidates to generate
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Prompt for generating refined candidates
|
||||||
|
"""
|
||||||
|
import config
|
||||||
|
pool_size = pool_size or config.GENERATION_POOL_SIZE
|
||||||
|
|
||||||
|
rejected_text = ""
|
||||||
|
if rejected_instructions:
|
||||||
|
rejected_formatted = "\n".join(f"- {inst}" for inst in rejected_instructions)
|
||||||
|
rejected_text = f"""
|
||||||
|
**用户未选择的指令(避免这些方向):**
|
||||||
|
{rejected_formatted}
|
||||||
|
"""
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
你是一个"System Prompt 改进助手"。
|
||||||
|
目标任务描述:
|
||||||
|
【{task_description}】
|
||||||
|
|
||||||
|
**用户选择的指令(基于此改进):**
|
||||||
|
{selected_instruction}
|
||||||
|
{rejected_text}
|
||||||
|
|
||||||
|
请基于用户选择的指令,生成 {pool_size} 条改进版本。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 每条指令必须以角色定义开头(例如:"你是一个..."、"你是..."等)
|
||||||
|
2. 保留用户选择指令的核心优点
|
||||||
|
3. 每条指令必须全面覆盖任务的所有要求和细节
|
||||||
|
4. 指令应清晰、具体、可执行,能够有效指导LLM完成任务
|
||||||
|
5. 避免与未选择指令相似的方向
|
||||||
|
6. 每条指令单独成行,不包含编号或额外说明
|
||||||
|
7. 所有生成的指令必须使用简体中文
|
||||||
|
|
||||||
|
生成 {pool_size} 条改进后的指令:
|
||||||
|
"""
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ from .prompt_utils import (
|
|||||||
refine_instruction,
|
refine_instruction,
|
||||||
refine_instruction_with_history,
|
refine_instruction_with_history,
|
||||||
generate_initial_system_instruction_candidates,
|
generate_initial_system_instruction_candidates,
|
||||||
generate_optimized_system_instruction
|
generate_optimized_system_instruction,
|
||||||
|
refine_based_on_selection
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse_candidates(raw: str) -> list:
|
def parse_candidates(raw: str) -> list:
|
||||||
@@ -147,3 +148,46 @@ def evaluate_system_instruction(
|
|||||||
correct += 1
|
correct += 1
|
||||||
|
|
||||||
return correct / total
|
return correct / total
|
||||||
|
|
||||||
|
|
||||||
|
def refine_instruction_candidates(
|
||||||
|
task_description: str,
|
||||||
|
selected_instruction: str,
|
||||||
|
rejected_instructions: List[str],
|
||||||
|
top_k: int = config.TOP_K,
|
||||||
|
pool_size: int = None,
|
||||||
|
model_name: str = None
|
||||||
|
) -> List[str]:
|
||||||
|
"""
|
||||||
|
Simple refinement: Generate new candidates based on user's selection.
|
||||||
|
|
||||||
|
This is NOT OPRO - just straightforward iterative refinement.
|
||||||
|
User picks a favorite, we generate variations of it while avoiding rejected ones.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_description: Description of the task
|
||||||
|
selected_instruction: The instruction the user selected
|
||||||
|
rejected_instructions: The instructions the user didn't select
|
||||||
|
top_k: Number of diverse candidates to return
|
||||||
|
pool_size: Number of candidates to generate before clustering
|
||||||
|
model_name: Optional model name to use
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of refined instruction candidates
|
||||||
|
"""
|
||||||
|
pool_size = pool_size or config.GENERATION_POOL_SIZE
|
||||||
|
|
||||||
|
# Generate the refinement prompt
|
||||||
|
meta_prompt = refine_based_on_selection(
|
||||||
|
task_description,
|
||||||
|
selected_instruction,
|
||||||
|
rejected_instructions,
|
||||||
|
pool_size
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use LLM to generate refined candidates
|
||||||
|
raw = call_qwen(meta_prompt, temperature=0.9, max_tokens=1024, model_name=model_name)
|
||||||
|
|
||||||
|
# Parse and cluster
|
||||||
|
all_candidates = parse_candidates(raw)
|
||||||
|
return cluster_and_select(all_candidates, top_k=top_k)
|
||||||
|
|||||||
@@ -304,43 +304,57 @@
|
|||||||
createNewRun(msg);
|
createNewRun(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleContinueOptimize(selectedInstruction, selectedScore) {
|
async function handleContinueOptimize(selectedInstruction, allCandidates) {
|
||||||
if (!currentRunId || loading) return;
|
if (!currentRunId || loading || !selectedInstruction) return;
|
||||||
|
|
||||||
// First, evaluate the selected instruction to add it to trajectory
|
|
||||||
if (selectedInstruction) {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
// Add the selected instruction to trajectory
|
// Get rejected instructions (all except the selected one)
|
||||||
const res = await fetch(`${API_BASE}/opro/evaluate`, {
|
const rejectedInstructions = allCandidates
|
||||||
|
.map(c => c.instruction)
|
||||||
|
.filter(inst => inst !== selectedInstruction);
|
||||||
|
|
||||||
|
// Call the refinement endpoint
|
||||||
|
const res = await fetch(`${API_BASE}/opro/refine`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
run_id: currentRunId,
|
run_id: currentRunId,
|
||||||
instruction: selectedInstruction
|
selected_instruction: selectedInstruction,
|
||||||
|
rejected_instructions: rejectedInstructions
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
throw new Error(data.error || 'Failed to evaluate instruction');
|
throw new Error(data.error || 'Failed to refine instruction');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Evaluated instruction, score:', data.data.score);
|
// Add refined candidates to messages
|
||||||
|
const newMessage = {
|
||||||
|
role: 'assistant',
|
||||||
|
type: 'candidates',
|
||||||
|
iteration: data.data.iteration,
|
||||||
|
candidates: data.data.candidates
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages(prev => {
|
||||||
|
const updated = [...prev, newMessage];
|
||||||
|
// Save to session messages
|
||||||
|
setSessionMessages(prevSessions => ({
|
||||||
|
...prevSessions,
|
||||||
|
[currentSessionId]: updated
|
||||||
|
}));
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('评估指令失败: ' + err.message);
|
alert('优化失败: ' + err.message);
|
||||||
console.error('Error evaluating instruction:', err);
|
console.error('Error refining instruction:', err);
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then generate new candidates based on updated trajectory
|
|
||||||
await generateCandidates(currentRunId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExecute(instruction) {
|
function handleExecute(instruction) {
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
executeInstruction(instruction, '');
|
executeInstruction(instruction, '');
|
||||||
@@ -537,7 +551,7 @@
|
|||||||
),
|
),
|
||||||
React.createElement('div', { className: 'flex gap-2' },
|
React.createElement('div', { className: 'flex gap-2' },
|
||||||
React.createElement('button', {
|
React.createElement('button', {
|
||||||
onClick: () => handleContinueOptimize(cand.instruction, cand.score),
|
onClick: () => handleContinueOptimize(cand.instruction, msg.candidates),
|
||||||
disabled: loading,
|
disabled: loading,
|
||||||
className: 'px-4 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed transition-colors text-sm font-medium'
|
className: 'px-4 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed transition-colors text-sm font-medium'
|
||||||
}, '继续优化'),
|
}, '继续优化'),
|
||||||
|
|||||||
Reference in New Issue
Block a user