96 lines
4.2 KiB
Python
96 lines
4.2 KiB
Python
"""调用 screen agent,按它的回复和成功提交的动作写回状态
|
|
|
|
kind 由模型决定;代码只在"回复 done 但最后一次提交失败"时不采纳:最后想做的修改没有写入草稿,
|
|
模型却说已完成。这是保守做法,last_write_failed 只看工具结果,任务是否完成仍以模型的结论和评测为准
|
|
agent 正常结束却没有结论时(调用次数用尽、只回了文本或空回复)补一次只读收尾;取消和异常照常抛出,不收尾
|
|
"""
|
|
|
|
import json
|
|
|
|
from langchain_core.messages import HumanMessage
|
|
|
|
from app.agent.screen_agent import (
|
|
AgentReply,
|
|
finalize_reply,
|
|
get_screen_agent,
|
|
reached_call_limit,
|
|
)
|
|
from app.agent.tools import ScreenAgentContext, last_write_failed
|
|
from app.processor.base import BaseNode
|
|
from app.processor.history import history_messages
|
|
from app.processor.state import QueryState, run_data
|
|
from app.processor.summary import request_summary
|
|
from app.resources.component import get_catalog_cache
|
|
|
|
# 评测按这句话识别"草稿因最后一次提交失败没有交付",单独核对草稿本身是否满足要求
|
|
DRAFT_REJECTED = "修改没有通过校验,这次没有生效,可以换个说法再试"
|
|
|
|
|
|
def reply_update(result: dict, *, limit_hit: bool = False) -> QueryState:
|
|
"""把 agent 的运行结果转换成 QueryState 的更新"""
|
|
reply: AgentReply | None = result.get("structured_response")
|
|
if reply is None:
|
|
# 收尾也没有给出合法的结论;按真实原因说明,不再一律说超出上限
|
|
answer = (
|
|
"处理步骤超出上限,这次没有完成,修改没有生效,可以换个说法再试"
|
|
if limit_hit
|
|
else "这次没有得出结论,修改没有生效,可以换个说法再试"
|
|
)
|
|
return {"status": "failed", "answer": answer}
|
|
if reply.kind == "clarify":
|
|
return {"status": "waiting_for_input", "clarification": reply.clarification}
|
|
if reply.kind == "unable":
|
|
return {"status": "failed", "answer": reply.message}
|
|
if last_write_failed(result.get("messages", [])):
|
|
return {"status": "failed", "answer": DRAFT_REJECTED}
|
|
# 各次成功提交的动作按顺序拼接;clarify、unable 不交付,下次请求从原始大屏重新执行
|
|
return {
|
|
"status": "completed",
|
|
"actions": result.get("actions") or [],
|
|
"answer": reply.message,
|
|
}
|
|
|
|
|
|
class ScreenAgentNode(BaseNode):
|
|
name = "screen_agent_node"
|
|
|
|
async def process(self, state: QueryState) -> QueryState:
|
|
updated_state: QueryState = state.copy()
|
|
catalog = get_catalog_cache()
|
|
if catalog is None:
|
|
updated_state["status"] = "failed"
|
|
updated_state["answer"] = "物料目录未载入,暂时无法新建或修改大屏"
|
|
return updated_state
|
|
|
|
context = state["input_context"]
|
|
agent = await get_screen_agent(with_data=bool(context.data_files))
|
|
result = await agent.ainvoke(
|
|
{
|
|
# 会话历史作为此前的对话消息,本次请求是最后一条用户消息
|
|
"messages": [
|
|
*history_messages(context.history),
|
|
HumanMessage(
|
|
content=json.dumps(request_summary(context), ensure_ascii=False)
|
|
),
|
|
]
|
|
},
|
|
context=ScreenAgentContext(
|
|
screen=context.screen,
|
|
catalog=catalog,
|
|
data=run_data(),
|
|
),
|
|
)
|
|
limit_hit = False
|
|
if result.get("structured_response") is None:
|
|
limit_hit = reached_call_limit(result["messages"])
|
|
last = result["messages"][-1]
|
|
self.logger.warning(
|
|
f"agent 没有给出结论({'调用次数用尽' if limit_hit else '没有调用 AgentReply'}),收尾一次: "
|
|
f"{last.type} {str(last.content)[:500]} "
|
|
f"metadata={getattr(last, 'response_metadata', None)}"
|
|
)
|
|
reply = await finalize_reply(result, limit_hit=limit_hit)
|
|
result = {**result, "structured_response": reply}
|
|
updated_state.update(reply_update(result, limit_hit=limit_hit))
|
|
return updated_state
|