F2: delegate prompt tasks to native Reasoner or OpenCode backends
This commit is contained in:
@@ -102,7 +102,7 @@ class PromptBrain(BaseBrain):
|
||||
|
||||
def system_prompt(self, cfg: AssistantConfig) -> str:
|
||||
prompt = self._store.render(cfg.prompt) if self._dynamic_enabled else cfg.prompt
|
||||
if cfg.runtimeMode == "pipeline" and any(tool.type == "task" for tool in cfg.tools):
|
||||
if cfg.runtimeMode == "pipeline" and cfg.task_backend is not None:
|
||||
from services.brains.prompt_tasks import TASK_HINT
|
||||
prompt += "\n" + TASK_HINT
|
||||
return prompt
|
||||
@@ -158,20 +158,6 @@ class PromptBrain(BaseBrain):
|
||||
):
|
||||
logger.warning(f"跳过客户端未上报的工具: {tool.function_name}")
|
||||
continue
|
||||
if tool.type == "task":
|
||||
if cfg.runtimeMode != "pipeline":
|
||||
raise ValueError("后台任务工具仅支持提示词 Pipeline")
|
||||
from services.brains.prompt_tasks import PromptTaskBridge
|
||||
if self._task_bridge is not None:
|
||||
raise ValueError("最多绑定一个后台任务工具")
|
||||
self._task_bridge = PromptTaskBridge(cfg, runtime, self._tools)
|
||||
for schema, handler in self._task_bridge.functions(tool, binding_by_id[tool.id]):
|
||||
if schema.name in registered_names:
|
||||
raise ValueError("后台任务函数名与已有工具冲突")
|
||||
schemas.append(schema)
|
||||
registered_names.add(schema.name)
|
||||
runtime.llm.register_function(schema.name, handler, cancel_on_interruption=False)
|
||||
continue
|
||||
if tool.type == "system":
|
||||
schema, handler = self._make_system_tool(tool, runtime)
|
||||
elif tool.type in {"http", "mcp", "client"}:
|
||||
@@ -195,6 +181,17 @@ class PromptBrain(BaseBrain):
|
||||
handler,
|
||||
cancel_on_interruption=policy.cancel_on_interruption,
|
||||
)
|
||||
if cfg.task_backend is not None:
|
||||
if cfg.runtimeMode != "pipeline":
|
||||
raise ValueError("任务后端仅支持提示词 Pipeline")
|
||||
from services.brains.prompt_tasks import PromptTaskBridge
|
||||
self._task_bridge = PromptTaskBridge(cfg, runtime, self._tools)
|
||||
for schema, handler in self._task_bridge.functions():
|
||||
if schema.name in registered_names:
|
||||
raise ValueError("任务委派函数名与已有工具冲突")
|
||||
schemas.append(schema)
|
||||
registered_names.add(schema.name)
|
||||
runtime.llm.register_function(schema.name, handler, cancel_on_interruption=False)
|
||||
runtime.set_tools(schemas)
|
||||
|
||||
async def close(self):
|
||||
|
||||
@@ -7,7 +7,8 @@ from services.background_tasks.records import TERMINAL
|
||||
|
||||
CONTROL_NAMES = {"get_background_task", "cancel_background_task", "send_background_task_message"}
|
||||
TASK_HINT = """
|
||||
你可以把需要远程 coding agent 执行的工作交给后台任务工具。提交回执仅表示已接受,
|
||||
你负责及时交流、澄清需求和播报进度;复杂推理或执行任务可调用 delegate_task 委派给任务后端。
|
||||
任务后端拥有独立上下文,请在任务要求中提供完整目标和必要背景。提交回执仅表示已接受,
|
||||
不能据此宣称代码已经修改或测试通过。后台执行时继续回答用户;查询进度用任务查询工具。
|
||||
用户明确要求停止时调用取消工具,普通插话不会停止任务。收到后台任务结果后用简洁口语
|
||||
告知用户一次,依据实际结果描述成功或失败。任务状态未知时如实告知,不要重复提交。
|
||||
@@ -24,19 +25,40 @@ class PromptTaskBridge:
|
||||
def close(self):
|
||||
self._detach()
|
||||
|
||||
def functions(self, tool, binding):
|
||||
properties, required = self.executor.schema_parts(tool, binding)
|
||||
def functions(self):
|
||||
backend = self.cfg.task_backend
|
||||
if backend is None:
|
||||
raise ValueError("助手未绑定任务后端")
|
||||
source = backend.instruction_source or {"source": "model"}
|
||||
model_supplied = source.get("source") == "model"
|
||||
properties = {"instruction": {"type": "string", "description": "完整任务目标、必要背景和验收要求"}} if model_supplied else {}
|
||||
required = ["instruction"] if model_supplied else []
|
||||
|
||||
async def submit(params):
|
||||
if self.runtime.tool_executor_factory is not None:
|
||||
await params.result_callback({"status": "error", "message": "自动测试不启动真实后台任务,请使用语音调试验证"})
|
||||
return
|
||||
try:
|
||||
arguments = self.executor.resolve_arguments(tool, binding, params.arguments or {})
|
||||
kind = source.get("source")
|
||||
if kind == "model":
|
||||
instruction = (params.arguments or {}).get("instruction")
|
||||
elif kind == "literal":
|
||||
instruction = source.get("value")
|
||||
elif kind == "variable":
|
||||
instruction = self.executor.store.value(str(source.get("variable_name") or source.get("variableName") or ""))
|
||||
elif kind == "template":
|
||||
instruction = self.executor.store.render_data(source.get("value"))
|
||||
else:
|
||||
raise ValueError("不支持的任务指令来源")
|
||||
if not isinstance(instruction, str):
|
||||
raise ValueError("任务指令必须是字符串")
|
||||
result = await self.service.submit(
|
||||
assistant_id=self.cfg.dynamic_variables.get("system__agent_id", ""),
|
||||
conversation_id=self.cfg.conversation_id,
|
||||
tool=tool, instruction=arguments["instruction"], invocation_id=params.tool_call_id,
|
||||
backend=backend, instruction=instruction, invocation_id=params.tool_call_id,
|
||||
variable_state={"values": self.executor.store.values,
|
||||
"definitions": self.cfg.dynamic_variable_definitions,
|
||||
"secrets": self.executor.store.secrets},
|
||||
)
|
||||
await params.result_callback({"status": "accepted", "task": result})
|
||||
except ValueError as exc:
|
||||
@@ -64,10 +86,10 @@ class PromptTaskBridge:
|
||||
except ValueError as exc:
|
||||
await params.result_callback({"status": "error", "message": str(exc)})
|
||||
|
||||
schemas = [(FunctionSchema(name=tool.function_name, description=tool.description or "把编程工作交给远程后台任务执行,立即返回任务编号", properties=properties, required=required), submit)]
|
||||
schemas = [(FunctionSchema(name="delegate_task", description="委派复杂推理或执行任务给已配置的任务后端,立即返回任务编号", properties=properties, required=required), submit)]
|
||||
for name, description in (
|
||||
("get_background_task", "查询当前助手后台任务的真实进度和结果"),
|
||||
("cancel_background_task", "用户明确要求停止时,请求停止远程后台任务"),
|
||||
("cancel_background_task", "用户明确要求停止时,请求停止后台任务"),
|
||||
("send_background_task_message", "回答后台任务当前提出的一个问题;权限确认请用户在网页操作"),
|
||||
):
|
||||
props = {"task_id": {"type": "string", "description": "提交回执中的任务id"}}
|
||||
|
||||
Reference in New Issue
Block a user