fix: role_agent_run 切原生 function calling(deliver/ask 作工具 + tool_calls 循环)
This commit is contained in:
parent
262d678e2a
commit
edd0a946a4
@ -172,6 +172,7 @@ AGENT_TOOLS = [
|
||||
{"name":"git_status","description":"查看git仓库状态","params":{"repo_dir":"仓库子目录(可选,默认repos下第一个)"}},
|
||||
{"name":"git_commit_push","description":"git add + commit + push","params":{"message":"提交信息","repo_dir":"仓库子目录(可选)"}},
|
||||
{"name":"ask_question","description":"向用户提问(缺少信息时使用)","params":{"question":"问题"}},
|
||||
{"name":"deliver","description":"提交最终交付件(代码文件已用write_file写好、git已提交时调用)","params":{"deliverable_type":"交付件类型(如code_files/design_doc)","summary":"概述","result":"交付件正文","files":"JSON数组[{\"path\":\"repos/仓库/src/x.py\",\"content\":\"代码内容\"}](可选)","git_commit_message":"git提交信息(可选)"}},
|
||||
]
|
||||
|
||||
AGENT_SYSTEM_PROMPT = """你是软件开发产线中的「__ROLE__」角色Agent。
|
||||
@ -510,6 +511,28 @@ def _resolve_repo_target(workspace_dir, repo_dir):
|
||||
return workspace_dir
|
||||
|
||||
|
||||
def _agent_tools_to_openai_schema(agent_tools):
|
||||
"""把 v1 AGENT_TOOLS({name,description,params})转成 OpenAI function-calling schema。"""
|
||||
return [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": t["name"],
|
||||
"description": t["description"],
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
k: {"type": "string", "description": v}
|
||||
for k, v in (t.get("params") or {}).items()
|
||||
},
|
||||
"required": list((t.get("params") or {}).keys()),
|
||||
},
|
||||
},
|
||||
}
|
||||
for t in agent_tools
|
||||
]
|
||||
|
||||
|
||||
async def _exec_agent_tool(tool, params, workspace_dir):
|
||||
p = params or {}
|
||||
try:
|
||||
@ -619,22 +642,51 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
msgs = [{"role": "system", "content": system}]
|
||||
msgs.append({"role": "user", "content": f"执行任务:{title}\n参数:{params_str}"})
|
||||
|
||||
from .llm_bridge import llm_call_msgs
|
||||
from .llm_bridge import llm_call_msgs, llm_call_msgs_native
|
||||
tools_schema = _agent_tools_to_openai_schema(AGENT_TOOLS)
|
||||
|
||||
deliverable = None
|
||||
ask_question = None
|
||||
|
||||
# ── Tool Loop ──
|
||||
# ── Tool Loop(原生 function calling)──
|
||||
for turn in range(15):
|
||||
try:
|
||||
raw = await llm_call_msgs(msgs, model=model_name, temperature=0.4)
|
||||
resp = await llm_call_msgs_native(msgs, tools=tools_schema, model=model_name, temperature=0.4)
|
||||
except Exception as e:
|
||||
await sor.sqlExe("UPDATE pipeline_tasks SET state='failed' WHERE id=${tid}$", {"tid": task_id})
|
||||
logger.error(f"role_agent_run llm failed: task={task_id} err={e}")
|
||||
return {"status": "failed", "task_id": task_id, "error": str(e)[:200]}
|
||||
|
||||
act = _parse_agent_action(raw)
|
||||
native_calls = (resp.get("tool_calls") or []) if isinstance(resp, dict) else []
|
||||
|
||||
if native_calls:
|
||||
# 回填 assistant(含 tool_calls,OpenAI 原生格式)
|
||||
msgs.append({"role": "assistant", "content": resp.get("content") or None, "tool_calls": native_calls})
|
||||
for tc in native_calls:
|
||||
fn = tc.get("function", {}) if isinstance(tc, dict) else {}
|
||||
tool = fn.get("name", "")
|
||||
try:
|
||||
params = json.loads(fn.get("arguments") or "{}")
|
||||
except Exception:
|
||||
params = {}
|
||||
|
||||
if tool == "deliver":
|
||||
deliverable = {"action": "deliver", **params}
|
||||
break
|
||||
if tool == "ask_question":
|
||||
ask_question = params.get("question", "")
|
||||
break
|
||||
|
||||
result = await _exec_agent_tool(tool, params, workspace_dir)
|
||||
msgs.append({"role": "tool", "tool_call_id": tc.get("id", ""), "content": str(result)})
|
||||
logger.info(f"role_agent tool_call: {tool} -> {str(result)[:100]}")
|
||||
if deliverable or ask_question:
|
||||
break
|
||||
continue
|
||||
|
||||
# 无 tool_calls → 文本兜底(deliver/ask 的 JSON)
|
||||
raw = resp.get("content", "") if isinstance(resp, dict) else str(resp)
|
||||
act = _parse_agent_action(raw)
|
||||
if act.get('action') == 'deliver':
|
||||
deliverable = act
|
||||
break
|
||||
@ -647,7 +699,7 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
result = await _exec_agent_tool(tool, params, workspace_dir)
|
||||
msgs.append({"role": "assistant", "content": raw})
|
||||
msgs.append({"role": "user", "content": f"工具 {tool} 结果:\n{result}"})
|
||||
logger.info(f"role_agent tool_call: {tool} -> {result[:100]}")
|
||||
logger.info(f"role_agent tool_call(text): {tool} -> {str(result)[:100]}")
|
||||
else:
|
||||
deliverable = {"result": raw}
|
||||
break
|
||||
@ -671,6 +723,11 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
# 写入代码文件
|
||||
files_written = []
|
||||
code_files = deliverable.get("files") or []
|
||||
if isinstance(code_files, str):
|
||||
try:
|
||||
code_files = json.loads(code_files)
|
||||
except Exception:
|
||||
code_files = []
|
||||
if isinstance(code_files, list):
|
||||
for f in code_files:
|
||||
if isinstance(f, dict) and f.get("path") and f.get("content"):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user