feat:增强需求澄清与任务管理功能
更新了 .env.example,新增聊天模型配置,以提升对话处理能力。 增强了 README.md,反映了包括需求澄清、代码复用和自动重试在内的新功能。 重构了 agent.py,以支持多模型交互,并为无法在本地执行的任务新增了引导处理逻辑。 改进了 SandboxRunner,增加了任务执行成功校验,并加入了工作区清理功能。 扩展了 HistoryManager,支持任务摘要生成以及记录的批量删除。 优化了 chat_view.py 和 history_view.py 中的 UI 组件,提升用户体验,包括 Markdown 渲染和任务管理选项。
This commit is contained in:
696
app/agent.py
696
app/agent.py
@@ -7,23 +7,30 @@ import os
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
from typing import Optional, Dict, Any, Tuple, List
|
||||
import threading
|
||||
import queue
|
||||
|
||||
from llm.client import get_client, LLMClientError
|
||||
from llm.prompts import (
|
||||
EXECUTION_PLAN_SYSTEM, EXECUTION_PLAN_USER,
|
||||
CODE_GENERATION_SYSTEM, CODE_GENERATION_USER
|
||||
CODE_GENERATION_SYSTEM, CODE_GENERATION_USER,
|
||||
TASK_SUMMARY_SYSTEM, TASK_SUMMARY_USER,
|
||||
CODE_FIX_SYSTEM, CODE_FIX_USER,
|
||||
REQUIREMENT_CHECK_SYSTEM, REQUIREMENT_CHECK_USER,
|
||||
REQUIREMENT_CLARIFY_SYSTEM, REQUIREMENT_CLARIFY_USER,
|
||||
REQUIREMENT_STRUCTURE_SYSTEM, REQUIREMENT_STRUCTURE_USER
|
||||
)
|
||||
from intent.classifier import classify_intent, IntentResult
|
||||
from intent.labels import CHAT, EXECUTION
|
||||
from intent.labels import CHAT, EXECUTION, GUIDANCE
|
||||
from safety.rule_checker import check_code_safety
|
||||
from safety.llm_reviewer import review_code_safety, LLMReviewResult
|
||||
from executor.sandbox_runner import SandboxRunner, ExecutionResult
|
||||
from ui.chat_view import ChatView
|
||||
from ui.task_guide_view import TaskGuideView
|
||||
from ui.history_view import HistoryView
|
||||
from ui.settings_view import SettingsView
|
||||
from ui.clarify_view import ClarifyView
|
||||
from history.manager import get_history_manager, HistoryManager
|
||||
|
||||
|
||||
@@ -55,6 +62,15 @@ class LocalAgentApp:
|
||||
self.chat_view: Optional[ChatView] = None
|
||||
self.task_view: Optional[TaskGuideView] = None
|
||||
self.history_view: Optional[HistoryView] = None
|
||||
self.settings_view: Optional[SettingsView] = None
|
||||
self.clarify_view: Optional[ClarifyView] = None
|
||||
|
||||
# 需求澄清状态
|
||||
self._clarify_state: Optional[Dict[str, Any]] = None
|
||||
|
||||
# 对话上下文(用于多轮对话)
|
||||
self._chat_context: List[Dict[str, str]] = []
|
||||
self._max_context_length: int = 10 # 最多保留的对话轮数
|
||||
|
||||
# 初始化 UI
|
||||
self._init_ui()
|
||||
@@ -63,7 +79,8 @@ class LocalAgentApp:
|
||||
"""初始化 UI"""
|
||||
self.root = tk.Tk()
|
||||
self.root.title("LocalAgent - 本地 AI 助手")
|
||||
self.root.geometry("800x700")
|
||||
self.root.geometry("1100x750")
|
||||
self.root.minsize(900, 600)
|
||||
self.root.configure(bg='#1e1e1e')
|
||||
|
||||
# 设置窗口图标(如果有的话)
|
||||
@@ -80,9 +97,13 @@ class LocalAgentApp:
|
||||
self.chat_view = ChatView(
|
||||
self.main_container,
|
||||
self._on_user_input,
|
||||
on_show_history=self._show_history
|
||||
on_show_history=self._show_history,
|
||||
on_show_settings=self._show_settings
|
||||
)
|
||||
|
||||
# 设置清空上下文的回调
|
||||
self.chat_view.set_clear_context_callback(self._clear_chat_context)
|
||||
|
||||
# 定期检查后台任务结果
|
||||
self._check_queue()
|
||||
|
||||
@@ -136,6 +157,9 @@ class LocalAgentApp:
|
||||
if intent_result.label == CHAT:
|
||||
# 对话模式
|
||||
self._handle_chat(user_input, intent_result)
|
||||
elif intent_result.label == GUIDANCE:
|
||||
# 操作指导模式
|
||||
self._handle_guidance(user_input, intent_result)
|
||||
else:
|
||||
# 执行模式
|
||||
self._handle_execution(user_input, intent_result)
|
||||
@@ -147,17 +171,24 @@ class LocalAgentApp:
|
||||
'system'
|
||||
)
|
||||
|
||||
# 添加用户消息到上下文
|
||||
self._chat_context.append({"role": "user", "content": user_input})
|
||||
|
||||
# 开始流式消息
|
||||
self.chat_view.start_stream_message('assistant')
|
||||
|
||||
# 在后台线程调用 LLM(流式)
|
||||
def do_chat_stream():
|
||||
client = get_client()
|
||||
model = os.getenv("GENERATION_MODEL_NAME")
|
||||
# 使用专门的对话模型,如果未配置则使用代码生成模型
|
||||
model = os.getenv("CHAT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
# 构建带上下文的消息列表
|
||||
messages = self._build_chat_messages()
|
||||
|
||||
full_response = []
|
||||
for chunk in client.chat_stream(
|
||||
messages=[{"role": "user", "content": user_input}],
|
||||
messages=messages,
|
||||
model=model,
|
||||
temperature=0.7,
|
||||
max_tokens=2048,
|
||||
@@ -184,16 +215,94 @@ class LocalAgentApp:
|
||||
|
||||
if error:
|
||||
self.chat_view.add_message(f"对话失败: {str(error)}", 'error')
|
||||
elif response:
|
||||
# 保存助手回复到上下文
|
||||
self._chat_context.append({"role": "assistant", "content": response})
|
||||
# 限制上下文长度
|
||||
self._trim_chat_context()
|
||||
|
||||
self.chat_view.set_input_enabled(True)
|
||||
|
||||
def _build_chat_messages(self) -> List[Dict[str, str]]:
|
||||
"""构建带上下文的消息列表"""
|
||||
system_prompt = """你是一个智能助手,可以回答各种问题。请用中文回答。
|
||||
|
||||
如果用户的问题涉及之前的对话内容,请结合上下文进行回答。"""
|
||||
|
||||
messages = [{"role": "system", "content": system_prompt}]
|
||||
messages.extend(self._chat_context)
|
||||
return messages
|
||||
|
||||
def _trim_chat_context(self) -> None:
|
||||
"""限制对话上下文长度"""
|
||||
# 每轮对话包含 user 和 assistant 两条消息
|
||||
max_messages = self._max_context_length * 2
|
||||
if len(self._chat_context) > max_messages:
|
||||
# 保留最近的消息
|
||||
self._chat_context = self._chat_context[-max_messages:]
|
||||
|
||||
def _clear_chat_context(self) -> None:
|
||||
"""清空对话上下文"""
|
||||
self._chat_context = []
|
||||
|
||||
def _handle_guidance(self, user_input: str, intent_result: IntentResult) -> None:
|
||||
"""处理操作指导任务(无法通过本地代码完成的任务)"""
|
||||
self.chat_view.add_message(
|
||||
f"识别为操作指导 (原因: {intent_result.reason})\n该任务无法通过本地代码完成,将为您提供操作指导。",
|
||||
'system'
|
||||
)
|
||||
|
||||
# 添加用户消息到上下文
|
||||
self._chat_context.append({"role": "user", "content": user_input})
|
||||
|
||||
# 开始流式消息
|
||||
self.chat_view.start_stream_message('assistant')
|
||||
|
||||
# 在后台线程调用 LLM(流式)
|
||||
def do_guidance_stream():
|
||||
client = get_client()
|
||||
model = os.getenv("CHAT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
# 构建专门的操作指导 Prompt
|
||||
system_prompt = """你是一个操作指导助手。用户询问的是一个无法通过本地Python代码完成的任务(如软件设置、系统配置、GUI操作等)。
|
||||
|
||||
请提供清晰、详细的操作步骤指导:
|
||||
1. 使用编号列表,步骤清晰
|
||||
2. 如果有多种方法,列出最常用的1-2种
|
||||
3. 如果涉及不同操作系统/软件版本,说明适用范围
|
||||
4. 可以适当配合说明截图位置或界面元素名称
|
||||
5. 如果操作有风险,给出提醒
|
||||
|
||||
用中文回答。"""
|
||||
|
||||
# 构建带上下文的消息列表
|
||||
messages = [{"role": "system", "content": system_prompt}]
|
||||
messages.extend(self._chat_context)
|
||||
|
||||
full_response = []
|
||||
for chunk in client.chat_stream(
|
||||
messages=messages,
|
||||
model=model,
|
||||
temperature=0.7,
|
||||
max_tokens=2048,
|
||||
timeout=300
|
||||
):
|
||||
full_response.append(chunk)
|
||||
self.result_queue.put((self._on_chat_chunk, (chunk,)))
|
||||
|
||||
return ''.join(full_response)
|
||||
|
||||
self._run_in_thread(
|
||||
do_guidance_stream,
|
||||
self._on_chat_complete
|
||||
)
|
||||
|
||||
def _handle_execution(self, user_input: str, intent_result: IntentResult):
|
||||
"""处理执行任务"""
|
||||
self.chat_view.add_message(
|
||||
f"识别为执行任务 (置信度: {intent_result.confidence:.0%})\n原因: {intent_result.reason}",
|
||||
'system'
|
||||
)
|
||||
self.chat_view.show_loading("正在生成执行计划")
|
||||
|
||||
# 保存用户输入和意图结果
|
||||
self.current_task = {
|
||||
@@ -201,10 +310,36 @@ class LocalAgentApp:
|
||||
'intent_result': intent_result
|
||||
}
|
||||
|
||||
# 在后台线程生成执行计划
|
||||
# 先查找是否有相似的成功任务
|
||||
similar_record = self.history.find_similar_success(user_input)
|
||||
if similar_record:
|
||||
# 询问用户是否复用
|
||||
task_desc = similar_record.task_summary or similar_record.user_input[:50]
|
||||
msg = (
|
||||
f"发现相似的成功任务:\n\n"
|
||||
f"任务: {task_desc}\n"
|
||||
f"时间: {similar_record.timestamp}\n\n"
|
||||
f"是否直接复用该任务的代码?\n"
|
||||
f"(选择[否]将生成新代码)"
|
||||
)
|
||||
result = messagebox.askyesno("发现相似任务", msg, icon='question')
|
||||
if result:
|
||||
# 复用代码
|
||||
self.current_task['execution_plan'] = similar_record.execution_plan
|
||||
self.current_task['code'] = similar_record.code
|
||||
self.current_task['task_summary'] = similar_record.task_summary
|
||||
self.current_task['is_reuse'] = True
|
||||
|
||||
self.chat_view.add_message("复用历史成功代码,请确认执行", 'system')
|
||||
self._show_task_guide()
|
||||
return
|
||||
|
||||
self.chat_view.show_loading("正在分析需求完整性")
|
||||
|
||||
# 检查需求是否完整
|
||||
self._run_in_thread(
|
||||
self._generate_execution_plan,
|
||||
self._on_plan_generated,
|
||||
self._check_requirement_completeness,
|
||||
self._on_requirement_checked,
|
||||
user_input
|
||||
)
|
||||
|
||||
@@ -224,10 +359,374 @@ class LocalAgentApp:
|
||||
self._run_in_thread(
|
||||
self._generate_code,
|
||||
self._on_code_generated,
|
||||
self.current_task['user_input'],
|
||||
self.current_task.get('structured_requirement') or self.current_task['user_input'],
|
||||
plan
|
||||
)
|
||||
|
||||
def _check_requirement_completeness(self, user_input: str) -> Dict[str, Any]:
|
||||
"""检查需求是否完整"""
|
||||
import json
|
||||
|
||||
client = get_client()
|
||||
model = os.getenv("CHAT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
response = client.chat(
|
||||
messages=[
|
||||
{"role": "system", "content": REQUIREMENT_CHECK_SYSTEM},
|
||||
{"role": "user", "content": REQUIREMENT_CHECK_USER.format(user_input=user_input)}
|
||||
],
|
||||
model=model,
|
||||
temperature=0.3,
|
||||
max_tokens=500,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
# 解析 JSON 响应
|
||||
try:
|
||||
# 尝试提取 JSON
|
||||
json_match = response
|
||||
if '```' in response:
|
||||
import re
|
||||
match = re.search(r'```(?:json)?\s*(.*?)\s*```', response, re.DOTALL)
|
||||
if match:
|
||||
json_match = match.group(1)
|
||||
|
||||
result = json.loads(json_match)
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
# 解析失败,默认认为需求完整
|
||||
return {
|
||||
"is_complete": True,
|
||||
"confidence": 0.5,
|
||||
"reason": "无法解析完整性检查结果",
|
||||
"suggested_defaults": {}
|
||||
}
|
||||
|
||||
def _on_requirement_checked(self, result: Optional[Dict], error: Optional[Exception]):
|
||||
"""需求完整性检查完成回调"""
|
||||
if error:
|
||||
# 检查失败,继续正常流程
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message(f"需求分析失败,将直接生成代码: {str(error)}", 'system')
|
||||
self._continue_to_code_generation()
|
||||
return
|
||||
|
||||
is_complete = result.get('is_complete', True)
|
||||
confidence = result.get('confidence', 1.0)
|
||||
|
||||
# 如果需求完整或置信度较高,直接继续
|
||||
if is_complete and confidence >= 0.7:
|
||||
self.chat_view.hide_loading()
|
||||
# 保存建议的默认值
|
||||
self.current_task['suggested_defaults'] = result.get('suggested_defaults', {})
|
||||
self._continue_to_code_generation()
|
||||
else:
|
||||
# 需求不完整,启动澄清流程
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message(
|
||||
f"需求信息不完整 (原因: {result.get('reason', '缺少关键信息')})\n正在启动需求澄清...",
|
||||
'system'
|
||||
)
|
||||
self._start_clarification()
|
||||
|
||||
def _continue_to_code_generation(self):
|
||||
"""继续代码生成流程"""
|
||||
self.chat_view.show_loading("正在生成任务摘要")
|
||||
|
||||
# 在后台线程生成任务摘要
|
||||
self._run_in_thread(
|
||||
self._generate_task_summary,
|
||||
self._on_summary_generated,
|
||||
self.current_task.get('structured_requirement') or self.current_task['user_input']
|
||||
)
|
||||
|
||||
def _start_clarification(self):
|
||||
"""启动需求澄清流程"""
|
||||
# 初始化澄清状态
|
||||
self._clarify_state = {
|
||||
'original_input': self.current_task['user_input'],
|
||||
'collected_info': {},
|
||||
'history': [],
|
||||
'current_question': None
|
||||
}
|
||||
|
||||
# 重置已显示的历史计数
|
||||
self._displayed_history_count = 0
|
||||
|
||||
self.chat_view.show_loading("正在生成澄清问题")
|
||||
|
||||
# 获取第一个澄清问题
|
||||
self._run_in_thread(
|
||||
self._get_clarify_question,
|
||||
self._on_clarify_question_received,
|
||||
self.current_task['user_input'],
|
||||
{},
|
||||
""
|
||||
)
|
||||
|
||||
def _get_clarify_question(self, user_input: str, collected_info: Dict, user_answer: str) -> Dict[str, Any]:
|
||||
"""获取澄清问题"""
|
||||
import json
|
||||
|
||||
client = get_client()
|
||||
model = os.getenv("CHAT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
# 格式化已收集的信息
|
||||
collected_str = json.dumps(collected_info, ensure_ascii=False, indent=2) if collected_info else "{}"
|
||||
|
||||
response = client.chat(
|
||||
messages=[
|
||||
{"role": "system", "content": REQUIREMENT_CLARIFY_SYSTEM},
|
||||
{"role": "user", "content": REQUIREMENT_CLARIFY_USER.format(
|
||||
user_input=user_input,
|
||||
collected_info=collected_str,
|
||||
user_answer=user_answer or "(首次询问)"
|
||||
)}
|
||||
],
|
||||
model=model,
|
||||
temperature=0.3,
|
||||
max_tokens=1000,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
# 解析 JSON 响应
|
||||
try:
|
||||
json_match = response
|
||||
if '```' in response:
|
||||
import re
|
||||
match = re.search(r'```(?:json)?\s*(.*?)\s*```', response, re.DOTALL)
|
||||
if match:
|
||||
json_match = match.group(1)
|
||||
|
||||
result = json.loads(json_match)
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
# 解析失败,认为不需要继续澄清
|
||||
return {
|
||||
"need_clarify": False,
|
||||
"question": "",
|
||||
"options": [],
|
||||
"collected_info": collected_info,
|
||||
"missing_info": []
|
||||
}
|
||||
|
||||
def _on_clarify_question_received(self, result: Optional[Dict], error: Optional[Exception]):
|
||||
"""收到澄清问题回调"""
|
||||
# 隐藏澄清视图的加载状态(如果有)
|
||||
if self.clarify_view:
|
||||
self.clarify_view.hide_loading()
|
||||
|
||||
if error:
|
||||
# 出错时切换回聊天界面
|
||||
if self.clarify_view:
|
||||
self.clarify_view.hide()
|
||||
self.clarify_view = None
|
||||
self.chat_view.get_frame().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
self.chat_view.add_message(f"获取澄清问题失败: {str(error)}", 'error')
|
||||
self._continue_to_code_generation()
|
||||
return
|
||||
|
||||
need_clarify = result.get('need_clarify', False)
|
||||
|
||||
if not need_clarify:
|
||||
# 不需要继续澄清,隐藏澄清视图,进行需求结构化
|
||||
if self.clarify_view:
|
||||
self.clarify_view.hide()
|
||||
self.clarify_view = None
|
||||
self._clarify_state['collected_info'].update(result.get('collected_info', {}))
|
||||
self._structure_requirement()
|
||||
else:
|
||||
# 继续显示/更新澄清视图
|
||||
self._show_clarify_view(result)
|
||||
|
||||
def _show_clarify_view(self, clarify_data: Dict):
|
||||
"""显示或更新需求澄清视图"""
|
||||
# 如果澄清视图不存在,创建新的
|
||||
if not self.clarify_view:
|
||||
# 隐藏聊天视图
|
||||
self.chat_view.get_frame().pack_forget()
|
||||
|
||||
# 创建澄清视图
|
||||
self.clarify_view = ClarifyView(
|
||||
self.main_container,
|
||||
on_submit=self._on_clarify_submit,
|
||||
on_cancel=self._on_clarify_cancel
|
||||
)
|
||||
self.clarify_view.show()
|
||||
|
||||
# 添加上一轮的历史记录(如果有新的)
|
||||
history = self._clarify_state.get('history', [])
|
||||
displayed_count = getattr(self, '_displayed_history_count', 0)
|
||||
|
||||
for item in history[displayed_count:]:
|
||||
self.clarify_view.add_history_item(item['question'], item['answer'])
|
||||
|
||||
self._displayed_history_count = len(history)
|
||||
|
||||
# 设置新问题和选项
|
||||
question = clarify_data.get('question', '请提供更多信息')
|
||||
options = clarify_data.get('options', [])
|
||||
|
||||
self.clarify_view.set_question(question, options)
|
||||
|
||||
# 更新已收集信息提示
|
||||
collected = self._clarify_state.get('collected_info', {})
|
||||
missing = clarify_data.get('missing_info', [])
|
||||
self.clarify_view.update_info_label(len(collected), len(collected) + len(missing))
|
||||
|
||||
# 保存当前问题
|
||||
self._clarify_state['current_question'] = question
|
||||
self._clarify_state['current_options'] = options
|
||||
|
||||
def _on_clarify_submit(self, answers: Dict[str, Any]):
|
||||
"""澄清问题提交回调"""
|
||||
# 格式化答案为字符串
|
||||
answer_parts = []
|
||||
for key, value in answers.items():
|
||||
if isinstance(value, list):
|
||||
answer_parts.append(f"{key}: {', '.join(value)}")
|
||||
else:
|
||||
answer_parts.append(f"{key}: {value}")
|
||||
answer_str = "; ".join(answer_parts)
|
||||
|
||||
# 保存到历史
|
||||
self._clarify_state['history'].append({
|
||||
'question': self._clarify_state.get('current_question', ''),
|
||||
'answer': answer_str
|
||||
})
|
||||
|
||||
# 更新已收集的信息
|
||||
self._clarify_state['collected_info'].update(answers)
|
||||
|
||||
# 在澄清视图中显示加载状态(不切换回聊天界面)
|
||||
if self.clarify_view:
|
||||
self.clarify_view.show_loading("正在分析您的回答...")
|
||||
|
||||
# 继续获取下一个问题
|
||||
self._run_in_thread(
|
||||
self._get_clarify_question,
|
||||
self._on_clarify_question_received,
|
||||
self._clarify_state['original_input'],
|
||||
self._clarify_state['collected_info'],
|
||||
answer_str
|
||||
)
|
||||
|
||||
def _on_clarify_cancel(self):
|
||||
"""取消澄清"""
|
||||
if self.clarify_view:
|
||||
self.clarify_view.hide()
|
||||
self.clarify_view = None
|
||||
|
||||
self._clarify_state = None
|
||||
self._displayed_history_count = 0
|
||||
self.current_task = None
|
||||
|
||||
self.chat_view.get_frame().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
self.chat_view.set_input_enabled(True)
|
||||
self.chat_view.add_message("已取消需求澄清", 'system')
|
||||
|
||||
def _structure_requirement(self):
|
||||
"""将澄清后的需求结构化"""
|
||||
self.chat_view.get_frame().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
self.chat_view.show_loading("正在整理需求")
|
||||
|
||||
self._run_in_thread(
|
||||
self._do_structure_requirement,
|
||||
self._on_requirement_structured
|
||||
)
|
||||
|
||||
def _do_structure_requirement(self) -> str:
|
||||
"""执行需求结构化"""
|
||||
import json
|
||||
|
||||
client = get_client()
|
||||
model = os.getenv("CHAT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
collected_str = json.dumps(
|
||||
self._clarify_state['collected_info'],
|
||||
ensure_ascii=False,
|
||||
indent=2
|
||||
)
|
||||
|
||||
response = client.chat_stream_collect(
|
||||
messages=[
|
||||
{"role": "system", "content": REQUIREMENT_STRUCTURE_SYSTEM},
|
||||
{"role": "user", "content": REQUIREMENT_STRUCTURE_USER.format(
|
||||
user_input=self._clarify_state['original_input'],
|
||||
collected_info=collected_str
|
||||
)}
|
||||
],
|
||||
model=model,
|
||||
temperature=0.3,
|
||||
max_tokens=1500,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
def _on_requirement_structured(self, result: Optional[str], error: Optional[Exception]):
|
||||
"""需求结构化完成回调"""
|
||||
if error:
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message(f"需求整理失败: {str(error)}", 'error')
|
||||
# 使用原始输入继续
|
||||
self._continue_to_code_generation()
|
||||
return
|
||||
|
||||
# 保存结构化的需求
|
||||
self.current_task['structured_requirement'] = result
|
||||
self.current_task['collected_info'] = self._clarify_state['collected_info']
|
||||
|
||||
# 清理澄清状态
|
||||
self._clarify_state = None
|
||||
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message("需求已明确,开始生成代码", 'system')
|
||||
|
||||
# 继续代码生成流程
|
||||
self._continue_to_code_generation()
|
||||
|
||||
def _generate_task_summary(self, user_input: str) -> str:
|
||||
"""生成任务摘要(使用小模型)"""
|
||||
client = get_client()
|
||||
# 使用意图识别模型(小模型)生成摘要
|
||||
model = os.getenv("INTENT_MODEL_NAME") or os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
response = client.chat(
|
||||
messages=[
|
||||
{"role": "system", "content": TASK_SUMMARY_SYSTEM},
|
||||
{"role": "user", "content": TASK_SUMMARY_USER.format(user_input=user_input)}
|
||||
],
|
||||
model=model,
|
||||
temperature=0.3,
|
||||
max_tokens=50,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
# 清理响应(去除引号、换行等)
|
||||
summary = response.strip().strip('"\'').strip()
|
||||
# 限制长度
|
||||
if len(summary) > 20:
|
||||
summary = summary[:20]
|
||||
|
||||
return summary
|
||||
|
||||
def _on_summary_generated(self, summary: Optional[str], error: Optional[Exception]):
|
||||
"""任务摘要生成完成回调"""
|
||||
if error:
|
||||
# 摘要生成失败不影响主流程,使用默认值
|
||||
summary = self.current_task['user_input'][:15] + "..."
|
||||
|
||||
self.current_task['task_summary'] = summary
|
||||
self.chat_view.update_loading_text("正在生成执行计划")
|
||||
|
||||
# 继续生成执行计划
|
||||
self._run_in_thread(
|
||||
self._generate_execution_plan,
|
||||
self._on_plan_generated,
|
||||
self.current_task['user_input']
|
||||
)
|
||||
|
||||
def _on_code_generated(self, result: tuple, error: Optional[Exception]):
|
||||
"""代码生成完成回调"""
|
||||
if error:
|
||||
@@ -298,6 +797,9 @@ class LocalAgentApp:
|
||||
self.current_task = None
|
||||
return
|
||||
|
||||
# 代码生成完成,清空 input 和 output 目录
|
||||
self.runner.clear_workspace(clear_input=True, clear_output=True)
|
||||
|
||||
self.chat_view.add_message("安全检查通过,请确认执行", 'system')
|
||||
|
||||
# 显示任务引导视图
|
||||
@@ -439,7 +941,8 @@ class LocalAgentApp:
|
||||
duration_ms=result.duration_ms,
|
||||
stdout=result.stdout,
|
||||
stderr=result.stderr,
|
||||
log_path=result.log_path
|
||||
log_path=result.log_path,
|
||||
task_summary=self.current_task.get('task_summary', '')
|
||||
)
|
||||
|
||||
self._show_execution_result(result)
|
||||
@@ -508,7 +1011,9 @@ class LocalAgentApp:
|
||||
self.history_view = HistoryView(
|
||||
self.main_container,
|
||||
self.history,
|
||||
on_back=self._hide_history
|
||||
on_back=self._hide_history,
|
||||
on_reuse_code=self._on_reuse_code,
|
||||
on_retry_task=self._on_retry_task
|
||||
)
|
||||
self.history_view.show()
|
||||
|
||||
@@ -520,6 +1025,169 @@ class LocalAgentApp:
|
||||
|
||||
self.chat_view.get_frame().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
def _on_reuse_code(self, record):
|
||||
"""复用历史记录中的代码"""
|
||||
from history.manager import TaskRecord
|
||||
|
||||
# 隐藏历史视图
|
||||
self._hide_history()
|
||||
|
||||
# 设置当前任务
|
||||
self.current_task = {
|
||||
'user_input': record.user_input,
|
||||
'intent_result': IntentResult(
|
||||
label=record.intent_label,
|
||||
confidence=record.intent_confidence,
|
||||
reason="复用历史任务"
|
||||
),
|
||||
'execution_plan': record.execution_plan,
|
||||
'code': record.code,
|
||||
'task_summary': record.task_summary,
|
||||
'is_reuse': True
|
||||
}
|
||||
|
||||
self.chat_view.add_message(f"复用历史任务: {record.task_summary or record.user_input[:30]}", 'system')
|
||||
self.chat_view.add_message("已加载历史代码,请确认执行", 'system')
|
||||
|
||||
# 直接显示任务引导视图(跳过代码生成)
|
||||
self._show_task_guide()
|
||||
|
||||
def _on_retry_task(self, record):
|
||||
"""重试失败的任务(AI 修复)"""
|
||||
from history.manager import TaskRecord
|
||||
|
||||
# 隐藏历史视图
|
||||
self._hide_history()
|
||||
|
||||
self.chat_view.add_message(f"重试任务: {record.task_summary or record.user_input[:30]}", 'system')
|
||||
self.chat_view.show_loading("正在分析错误并修复代码")
|
||||
self.chat_view.set_input_enabled(False)
|
||||
|
||||
# 保存任务信息
|
||||
self.current_task = {
|
||||
'user_input': record.user_input,
|
||||
'intent_result': IntentResult(
|
||||
label=record.intent_label,
|
||||
confidence=record.intent_confidence,
|
||||
reason="重试失败任务"
|
||||
),
|
||||
'execution_plan': record.execution_plan,
|
||||
'original_code': record.code,
|
||||
'original_stdout': record.stdout,
|
||||
'original_stderr': record.stderr,
|
||||
'task_summary': record.task_summary,
|
||||
'is_retry': True
|
||||
}
|
||||
|
||||
# 在后台线程修复代码
|
||||
self._run_in_thread(
|
||||
self._fix_code,
|
||||
self._on_code_fixed,
|
||||
record
|
||||
)
|
||||
|
||||
def _fix_code(self, record) -> tuple:
|
||||
"""修复失败的代码"""
|
||||
client = get_client()
|
||||
model = os.getenv("GENERATION_MODEL_NAME")
|
||||
|
||||
response = client.chat_stream_collect(
|
||||
messages=[
|
||||
{"role": "system", "content": CODE_FIX_SYSTEM},
|
||||
{"role": "user", "content": CODE_FIX_USER.format(
|
||||
user_input=record.user_input,
|
||||
execution_plan=record.execution_plan,
|
||||
code=record.code,
|
||||
stdout=record.stdout or "(无输出)",
|
||||
stderr=record.stderr or "(无错误信息)"
|
||||
)}
|
||||
],
|
||||
model=model,
|
||||
temperature=0.2,
|
||||
max_tokens=4096,
|
||||
timeout=300
|
||||
)
|
||||
|
||||
try:
|
||||
code = self._extract_code(response)
|
||||
return (code, None)
|
||||
except ValueError as e:
|
||||
return (None, e)
|
||||
|
||||
def _on_code_fixed(self, result: tuple, error: Optional[Exception]):
|
||||
"""代码修复完成回调"""
|
||||
if error:
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message(f"代码修复失败: {str(error)}", 'error')
|
||||
self.chat_view.set_input_enabled(True)
|
||||
self.current_task = None
|
||||
return
|
||||
|
||||
code, extract_error = result
|
||||
if extract_error:
|
||||
self.chat_view.hide_loading()
|
||||
self.chat_view.add_message(f"代码提取失败: {str(extract_error)}", 'error')
|
||||
self.chat_view.set_input_enabled(True)
|
||||
self.current_task = None
|
||||
return
|
||||
|
||||
self.current_task['code'] = code
|
||||
self.chat_view.update_loading_text("正在进行安全检查")
|
||||
|
||||
# 硬规则检查
|
||||
rule_result = check_code_safety(code)
|
||||
if not rule_result.passed:
|
||||
self.chat_view.hide_loading()
|
||||
violations = "\n".join(f" • {v}" for v in rule_result.violations)
|
||||
self.chat_view.add_message(
|
||||
f"修复后的代码安全检查未通过:\n{violations}",
|
||||
'error'
|
||||
)
|
||||
self.chat_view.set_input_enabled(True)
|
||||
self.current_task = None
|
||||
return
|
||||
|
||||
self.current_task['warnings'] = rule_result.warnings
|
||||
|
||||
# LLM 安全审查
|
||||
self._run_in_thread(
|
||||
lambda: review_code_safety(
|
||||
self.current_task['user_input'],
|
||||
self.current_task['execution_plan'],
|
||||
code,
|
||||
rule_result.warnings
|
||||
),
|
||||
self._on_safety_reviewed
|
||||
)
|
||||
|
||||
def _show_settings(self):
|
||||
"""显示设置视图"""
|
||||
# 隐藏聊天视图
|
||||
self.chat_view.get_frame().pack_forget()
|
||||
|
||||
# 创建设置视图
|
||||
self.settings_view = SettingsView(
|
||||
self.main_container,
|
||||
env_path=self.project_root / ".env",
|
||||
on_save=self._on_settings_saved,
|
||||
on_back=self._hide_settings
|
||||
)
|
||||
self.settings_view.show()
|
||||
|
||||
def _hide_settings(self):
|
||||
"""隐藏设置视图,返回聊天"""
|
||||
if self.settings_view:
|
||||
self.settings_view.hide()
|
||||
self.settings_view = None
|
||||
|
||||
self.chat_view.get_frame().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
def _on_settings_saved(self):
|
||||
"""设置保存后的回调"""
|
||||
# 配置已通过 set_key 保存并更新了环境变量
|
||||
# 可以在这里添加额外的处理逻辑
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
"""运行应用"""
|
||||
self.root.mainloop()
|
||||
|
||||
Reference in New Issue
Block a user