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_status","description":"查看git仓库状态","params":{"repo_dir":"仓库子目录(可选,默认repos下第一个)"}},
|
||||||
{"name":"git_commit_push","description":"git add + commit + push","params":{"message":"提交信息","repo_dir":"仓库子目录(可选)"}},
|
{"name":"git_commit_push","description":"git add + commit + push","params":{"message":"提交信息","repo_dir":"仓库子目录(可选)"}},
|
||||||
{"name":"ask_question","description":"向用户提问(缺少信息时使用)","params":{"question":"问题"}},
|
{"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。
|
AGENT_SYSTEM_PROMPT = """你是软件开发产线中的「__ROLE__」角色Agent。
|
||||||
@ -510,6 +511,28 @@ def _resolve_repo_target(workspace_dir, repo_dir):
|
|||||||
return workspace_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):
|
async def _exec_agent_tool(tool, params, workspace_dir):
|
||||||
p = params or {}
|
p = params or {}
|
||||||
try:
|
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 = [{"role": "system", "content": system}]
|
||||||
msgs.append({"role": "user", "content": f"执行任务:{title}\n参数:{params_str}"})
|
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
|
deliverable = None
|
||||||
ask_question = None
|
ask_question = None
|
||||||
|
|
||||||
# ── Tool Loop ──
|
# ── Tool Loop(原生 function calling)──
|
||||||
for turn in range(15):
|
for turn in range(15):
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
await sor.sqlExe("UPDATE pipeline_tasks SET state='failed' WHERE id=${tid}$", {"tid": task_id})
|
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}")
|
logger.error(f"role_agent_run llm failed: task={task_id} err={e}")
|
||||||
return {"status": "failed", "task_id": task_id, "error": str(e)[:200]}
|
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':
|
if act.get('action') == 'deliver':
|
||||||
deliverable = act
|
deliverable = act
|
||||||
break
|
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)
|
result = await _exec_agent_tool(tool, params, workspace_dir)
|
||||||
msgs.append({"role": "assistant", "content": raw})
|
msgs.append({"role": "assistant", "content": raw})
|
||||||
msgs.append({"role": "user", "content": f"工具 {tool} 结果:\n{result}"})
|
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:
|
else:
|
||||||
deliverable = {"result": raw}
|
deliverable = {"result": raw}
|
||||||
break
|
break
|
||||||
@ -671,6 +723,11 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
|||||||
# 写入代码文件
|
# 写入代码文件
|
||||||
files_written = []
|
files_written = []
|
||||||
code_files = deliverable.get("files") or []
|
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):
|
if isinstance(code_files, list):
|
||||||
for f in code_files:
|
for f in code_files:
|
||||||
if isinstance(f, dict) and f.get("path") and f.get("content"):
|
if isinstance(f, dict) and f.get("path") and f.get("content"):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user