修复BrowserController的截图缓存问题和浏览器权柄配置丢失问题
No files matched your search
@@ -2,18 +2,24 @@
|
||||
|
||||
系统定位:
|
||||
对外导出各 Agent 的 ``AgentRuntime`` 单例及多 Agent 运行时。
|
||||
所有 Agent 实例均从 ``agent_config.json`` 动态构建,不再 hardcoded。
|
||||
|
||||
Agent 列表:
|
||||
- main_agent_runtime: 主 Agent 运行时(单 Agent 模式)。
|
||||
- main_agent_runtime: 主 Agent 运行时(从配置中 role="main" 的 agent 动态创建)。
|
||||
- reload_all_agent_runtimes: 重新加载配置并重建所有 Agent 运行时。
|
||||
- build_multi_agent_runtime: 构建一主多子 Agent 运行时。
|
||||
- get_agent_graph_snapshot: 获取多 Agent 图状态快照。
|
||||
|
||||
可扩展性:
|
||||
子 Agent 实现后在此追加导出,如::
|
||||
from agent.agents.research_agent import research_agent_runtime
|
||||
__all__ = ["main_agent_runtime", "research_agent_runtime"]
|
||||
新增 Agent 只需在 agent_config.json 中添加配置项,无需编写 .py 文件。
|
||||
"""
|
||||
from agent.agents.main_agent import main_agent_runtime, reload_main_agent_runtime
|
||||
from agent.agents.agent import (
|
||||
build_all_agent_runtimes,
|
||||
get_main_agent_runtime,
|
||||
get_all_agent_runtimes,
|
||||
get_agent_runtime,
|
||||
reload_all_agent_runtimes,
|
||||
)
|
||||
from agent.agents.multi_agent_runtime import (
|
||||
MultiAgentRuntime,
|
||||
SubAgentConfig,
|
||||
@@ -21,9 +27,19 @@ from agent.agents.multi_agent_runtime import (
|
||||
get_agent_graph_snapshot,
|
||||
)
|
||||
|
||||
# 首次导入时从配置构建,并暴露为模块级属性(向后兼容旧导入路径)
|
||||
_built = build_all_agent_runtimes()
|
||||
main_agent_runtime = get_main_agent_runtime()
|
||||
reload_main_agent_runtime = reload_all_agent_runtimes # 向后兼容别名
|
||||
|
||||
__all__ = [
|
||||
"main_agent_runtime",
|
||||
"reload_main_agent_runtime",
|
||||
"reload_all_agent_runtimes",
|
||||
"build_all_agent_runtimes",
|
||||
"get_main_agent_runtime",
|
||||
"get_all_agent_runtimes",
|
||||
"get_agent_runtime",
|
||||
"MultiAgentRuntime",
|
||||
"SubAgentConfig",
|
||||
"build_multi_agent_runtime",
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
"""动态 Agent 工厂:根据 agent_config.json 全动态创建 AgentRuntime。
|
||||
|
||||
系统定位:
|
||||
替代原有的 hardcoded main_agent / code_agent / research_agent 等单文件,
|
||||
实现从配置文件全动态加载 Agent 实例。所有 Agent 的运行参数(图类型、
|
||||
模型、工具集、系统提示词、最大迭代次数)均由 agent_config.json 驱动。
|
||||
|
||||
使用方式:
|
||||
from agent.agents.agent import get_main_agent_runtime, reload_all_agent_runtimes
|
||||
runtime = get_main_agent_runtime()
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from agent.core.config_manager import load_agent_configs, load_models, AgentConfig
|
||||
from agent.core.graph import build_graph_by_name
|
||||
from agent.core.runtime import AgentRuntime
|
||||
from agent.tools import get_tools
|
||||
|
||||
|
||||
# ---------- 缓存 ----------
|
||||
_all_agent_runtimes: dict[str, AgentRuntime] = {}
|
||||
_main_agent_name: str = "main"
|
||||
|
||||
|
||||
def _build_llm_for_agent(cfg: AgentConfig):
|
||||
"""根据 Agent 配置中指定的 llm_model_id 构建 LLM 实例。
|
||||
|
||||
从 env_config.json 的 models 列表中查找匹配的模型配置,
|
||||
用其参数构造 ChatQwen 实例。若未找到匹配模型则回退到环境变量默认值。
|
||||
"""
|
||||
from agent.core.llm import ChatQwen
|
||||
|
||||
models = load_models()
|
||||
model_config = None
|
||||
for m in models:
|
||||
if m.get("id") == cfg.llm_model_id:
|
||||
model_config = m
|
||||
break
|
||||
|
||||
if model_config:
|
||||
return ChatQwen(
|
||||
model=model_config.get("model", ""),
|
||||
api_key=model_config.get("api_key", ""),
|
||||
base_url=model_config.get("base_url", ""),
|
||||
timeout=float(model_config.get("timeout", 60)),
|
||||
max_retries=int(model_config.get("max_retries", 0)),
|
||||
)
|
||||
# 回退:使用环境变量中已加载的默认 LLM 配置
|
||||
from agent.core.llm import llm as _default_llm
|
||||
return ChatQwen(
|
||||
model=_default_llm.model_name,
|
||||
api_key=_default_llm.openai_api_key or "",
|
||||
base_url=_default_llm.openai_api_base or "",
|
||||
)
|
||||
|
||||
|
||||
def _build_agent_runtime(cfg: AgentConfig) -> AgentRuntime:
|
||||
"""根据单个 AgentConfig 构建 AgentRuntime 实例。
|
||||
|
||||
输入:
|
||||
cfg: Agent 配置(name/graph/tools/llm_model_id/system_prompt/max_iterations)。
|
||||
|
||||
输出:
|
||||
完整的 AgentRuntime 实例,可被 execute_agent 直接使用。
|
||||
"""
|
||||
# 1. 构建 LLM
|
||||
agent_llm = _build_llm_for_agent(cfg)
|
||||
|
||||
# 2. 获取所有可用工具并按配置过滤
|
||||
all_tools = get_tools()
|
||||
if cfg.tools:
|
||||
agent_tools = [t for t in all_tools if t.name in cfg.tools]
|
||||
else:
|
||||
agent_tools = all_tools
|
||||
|
||||
# 3. bind_tools
|
||||
model_with_tools = agent_llm.bind_tools(agent_tools, tool_choice="auto")
|
||||
|
||||
# 4. 根据 graph 配置动态构建图
|
||||
graph = build_graph_by_name(
|
||||
graph_name=cfg.graph,
|
||||
tools=agent_tools,
|
||||
model_with_tools=model_with_tools,
|
||||
trajectory_rounds=cfg.trajectory_rounds,
|
||||
parallel_tools=cfg.parallel_tools,
|
||||
)
|
||||
|
||||
# 5. 组装 AgentRuntime
|
||||
return AgentRuntime(
|
||||
name=cfg.name,
|
||||
graph=graph,
|
||||
llm=agent_llm,
|
||||
tools=agent_tools,
|
||||
tools_by_name={tool.name: tool for tool in agent_tools},
|
||||
system_prompt=cfg.system_prompt,
|
||||
max_iterations=cfg.max_iterations,
|
||||
trajectory_rounds=cfg.trajectory_rounds,
|
||||
)
|
||||
|
||||
|
||||
def build_all_agent_runtimes() -> dict[str, AgentRuntime]:
|
||||
"""从 agent_config.json 读取所有启用 Agent 并构建运行时字典。
|
||||
|
||||
输出:
|
||||
{agent_name: AgentRuntime} 字典,其中第一个 role="main" 的为 main_agent。
|
||||
|
||||
系统定位:
|
||||
启动时调用一次,构建全局运行时缓存。
|
||||
"""
|
||||
global _all_agent_runtimes, _main_agent_name
|
||||
configs = load_agent_configs()
|
||||
_all_agent_runtimes = {}
|
||||
_main_agent_name = "main"
|
||||
|
||||
for cfg in configs:
|
||||
if not cfg.enabled:
|
||||
continue
|
||||
try:
|
||||
runtime = _build_agent_runtime(cfg)
|
||||
_all_agent_runtimes[cfg.name] = runtime
|
||||
if cfg.role == "main":
|
||||
_main_agent_name = cfg.name
|
||||
except Exception as e:
|
||||
print(f"[AgentFactory] 构建 Agent '{cfg.name}' 失败: {e}")
|
||||
|
||||
# 确保至少有一个 main agent
|
||||
if _main_agent_name not in _all_agent_runtimes and _all_agent_runtimes:
|
||||
first_name = next(iter(_all_agent_runtimes))
|
||||
_main_agent_name = first_name
|
||||
print(f"[AgentFactory] 未找到 role='main' 的 Agent,自动将 '{first_name}' 设为主 Agent。")
|
||||
|
||||
return _all_agent_runtimes
|
||||
|
||||
|
||||
def get_main_agent_runtime() -> AgentRuntime | None:
|
||||
"""获取当前配置中 role="main" 的 AgentRuntime。
|
||||
|
||||
若运行时缓存为空则自动构建。
|
||||
"""
|
||||
global _all_agent_runtimes
|
||||
if not _all_agent_runtimes:
|
||||
build_all_agent_runtimes()
|
||||
return _all_agent_runtimes.get(_main_agent_name)
|
||||
|
||||
|
||||
def get_all_agent_runtimes() -> dict[str, AgentRuntime]:
|
||||
"""获取所有已构建的 Agent 运行时字典。"""
|
||||
if not _all_agent_runtimes:
|
||||
build_all_agent_runtimes()
|
||||
return dict(_all_agent_runtimes)
|
||||
|
||||
|
||||
def get_agent_runtime(name: str) -> AgentRuntime | None:
|
||||
"""按名称获取指定 Agent 运行时。"""
|
||||
if not _all_agent_runtimes:
|
||||
build_all_agent_runtimes()
|
||||
return _all_agent_runtimes.get(name)
|
||||
|
||||
|
||||
def reload_all_agent_runtimes() -> dict:
|
||||
"""重新加载配置并重建所有 Agent 运行时,返回当前状态。
|
||||
|
||||
输出:
|
||||
{"agents": [{"name": str, "tools_count": int, "graph": str, "max_iterations": int}, ...],
|
||||
"main_agent": str}
|
||||
"""
|
||||
global _all_agent_runtimes, _main_agent_name
|
||||
_all_agent_runtimes.clear()
|
||||
build_all_agent_runtimes()
|
||||
|
||||
# 同步更新 chatbot_agent 中的缓存引用
|
||||
import agent.chatbot_agent as ca
|
||||
main_rt = _all_agent_runtimes.get(_main_agent_name)
|
||||
if main_rt:
|
||||
ca.main_agent_runtime = main_rt
|
||||
ca.agent_graph = main_rt.graph
|
||||
ca.tools = main_rt.tools
|
||||
ca.TOOLS_BY_NAME = main_rt.tools_by_name
|
||||
ca.llm = main_rt.llm
|
||||
|
||||
agents_info = []
|
||||
for name, rt in _all_agent_runtimes.items():
|
||||
agents_info.append({
|
||||
"name": name,
|
||||
"tools_count": len(rt.tools),
|
||||
"graph": rt.graph.__class__.__name__ if rt.graph else "unknown",
|
||||
"max_iterations": rt.max_iterations,
|
||||
})
|
||||
|
||||
return {
|
||||
"agents": agents_info,
|
||||
"main_agent": _main_agent_name,
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
"""代码子 Agent(预留)。
|
||||
|
||||
系统定位:
|
||||
计划用于代码生成、静态分析、仓库操作等,可绑定代码执行沙箱工具
|
||||
与目录读写类工具。
|
||||
|
||||
可扩展性:
|
||||
可实现独立 AgentRuntime,或在 team 模式下由 main Agent 委派任务;
|
||||
建议对执行类工具保持 confirm 策略。
|
||||
"""
|
||||
@@ -1,11 +0,0 @@
|
||||
"""GUI子 Agent(预留)。
|
||||
|
||||
系统定位:
|
||||
计划用于GUI自动化、页面理解与操作编排,工具以 gui_control
|
||||
及 screen_capture 为主。
|
||||
|
||||
可扩展性:
|
||||
实现时可单独配置 tool_policy 白名单、更严格的 confirm 策略,
|
||||
并绑定专用 system_prompt 与较短上下文窗口。
|
||||
"""
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
"""主 Agent 装配入口:工具、模型、图与运行时单例。
|
||||
|
||||
系统定位:
|
||||
项目默认 Agent 实例 ``main_agent_runtime``,由 Web 层导入并调用
|
||||
``core.runtime.execute_agent`` / ``continue_after_human_action``。
|
||||
|
||||
装配内容:
|
||||
- get_tools(): 主 Agent 可用工具集
|
||||
- llm.bind_tools: 工具调用能力
|
||||
- build_agent_graph: 标准 ReAct 图
|
||||
- MAIN_SYSTEM_PROMPT: 系统提示词
|
||||
|
||||
可扩展性:
|
||||
- 可按环境变量切换工具子集或模型。
|
||||
- 子 Agent(research/browser/code)可仿照本文件独立装配 AgentRuntime。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from agent.core.graph_builder import build_agent_graph
|
||||
from agent.core.llm import llm
|
||||
from agent.core.runtime import AgentRuntime
|
||||
from agent.memory.system_prompt import MAIN_SYSTEM_PROMPT
|
||||
from agent.tools import get_tools
|
||||
|
||||
|
||||
def _build_main_agent_runtime():
|
||||
"""根据当前配置重新构建主 Agent 运行时。"""
|
||||
from agent.core.llm import llm as _llm
|
||||
_system_prompt = MAIN_SYSTEM_PROMPT
|
||||
_max_iterations = 200
|
||||
_tools = get_tools()
|
||||
try:
|
||||
from agent.core.config_manager import load_agent_configs
|
||||
configs = load_agent_configs()
|
||||
for cfg in configs:
|
||||
if cfg.name == "main" and cfg.enabled:
|
||||
if cfg.system_prompt:
|
||||
_system_prompt = cfg.system_prompt
|
||||
_max_iterations = cfg.max_iterations
|
||||
# 根据 agent_config.json 中的 tools 列表过滤工具
|
||||
if cfg.tools:
|
||||
_tools = [t for t in _tools if t.name in cfg.tools]
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
model_with_tools = _llm.bind_tools(_tools, tool_choice="auto")
|
||||
return AgentRuntime(
|
||||
name="main",
|
||||
graph=build_agent_graph(_tools, model_with_tools),
|
||||
llm=_llm,
|
||||
tools=_tools,
|
||||
tools_by_name={tool.name: tool for tool in _tools},
|
||||
system_prompt=_system_prompt,
|
||||
max_iterations=_max_iterations,
|
||||
)
|
||||
|
||||
|
||||
main_agent_runtime = _build_main_agent_runtime()
|
||||
|
||||
|
||||
def reload_main_agent_runtime() -> dict:
|
||||
"""重新加载配置并重建主 Agent 运行时,返回当前状态供调试。"""
|
||||
global main_agent_runtime
|
||||
import agent.chatbot_agent as ca
|
||||
main_agent_runtime = _build_main_agent_runtime()
|
||||
# 更新 chatbot_agent 中缓存的引用
|
||||
ca.main_agent_runtime = main_agent_runtime
|
||||
ca.agent_graph = main_agent_runtime.graph
|
||||
ca.tools = main_agent_runtime.tools
|
||||
ca.TOOLS_BY_NAME = main_agent_runtime.tools_by_name
|
||||
ca.llm = main_agent_runtime.llm
|
||||
return {
|
||||
"name": main_agent_runtime.name,
|
||||
"tools_count": len(main_agent_runtime.tools),
|
||||
"max_iterations": main_agent_runtime.max_iterations,
|
||||
}
|
||||
@@ -25,7 +25,7 @@ from agent.core.multi_agent_state import (
|
||||
ToolCallEvent,
|
||||
init_multi_agent_state,
|
||||
)
|
||||
from agent.tools.tool_agent_call import AgentCallTool
|
||||
from agent.tools.tool_agent_call import CallSubAgentTool
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -92,9 +92,9 @@ def build_multi_agent_runtime(
|
||||
|
||||
示例::
|
||||
|
||||
from agent.agents.main_agent import main_agent_runtime
|
||||
from agent.agents.agent import get_main_agent_runtime
|
||||
runtime = build_multi_agent_runtime(
|
||||
main_runtime=main_agent_runtime,
|
||||
main_runtime=get_main_agent_runtime(),
|
||||
sub_runtimes=[
|
||||
{"runtime": code_agent_runtime, "icon": "💻"},
|
||||
{"runtime": research_agent_runtime, "icon": "🔍"},
|
||||
@@ -104,9 +104,9 @@ def build_multi_agent_runtime(
|
||||
sub_runtimes = sub_runtimes or []
|
||||
|
||||
# 组装主 agent 工具(含 agent_call)
|
||||
main_tools = list(main_runtime.tools) + [AgentCallTool()]
|
||||
main_tools = list(main_runtime.tools) + [CallSubAgentTool()]
|
||||
main_tools_by_name = dict(main_runtime.tools_by_name)
|
||||
main_tools_by_name["agent_call"] = AgentCallTool()
|
||||
main_tools_by_name["agent_call"] = CallSubAgentTool()
|
||||
main_model_with_tools = main_runtime.llm.bind_tools(main_tools, tool_choice="auto")
|
||||
|
||||
# 子 agent 配置列表
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
"""研究子 Agent(预留)。
|
||||
|
||||
系统定位:
|
||||
计划用于深度检索、文献综述等专用任务,与 main Agent 通过 team 模式
|
||||
或子图委派协作。
|
||||
|
||||
可扩展性:
|
||||
实现步骤建议:
|
||||
1. 定义 RESEARCH_SYSTEM_PROMPT 与精简工具集(web_search、RAG 等)
|
||||
2. build_agent_graph(tools, model) 或自定义图(含检索节点)
|
||||
3. 导出 research_agent_runtime = AgentRuntime(...)
|
||||
"""
|
||||
@@ -2,12 +2,24 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from agent.agents.main_agent import main_agent_runtime
|
||||
from agent.agents.agent import (
|
||||
build_all_agent_runtimes,
|
||||
get_main_agent_runtime,
|
||||
reload_all_agent_runtimes,
|
||||
)
|
||||
from agent.core.runtime import continue_after_human_action as continue_runtime_after_human_action
|
||||
from agent.core.runtime import execute_agent as execute_runtime_agent
|
||||
|
||||
AgentMode = Literal["agent", "team"]
|
||||
|
||||
# 首次导入时从配置构建主 Agent 运行时并缓存引用
|
||||
_built = build_all_agent_runtimes()
|
||||
main_agent_runtime = get_main_agent_runtime()
|
||||
agent_graph = main_agent_runtime.graph
|
||||
tools = main_agent_runtime.tools
|
||||
TOOLS_BY_NAME = main_agent_runtime.tools_by_name
|
||||
llm = main_agent_runtime.llm
|
||||
|
||||
|
||||
def execute_agent(
|
||||
chat_history: list[dict[str, Any]],
|
||||
@@ -43,9 +55,3 @@ def continue_after_human_action(
|
||||
instruction=instruction,
|
||||
output_type=output_type,
|
||||
)
|
||||
|
||||
|
||||
agent_graph = main_agent_runtime.graph
|
||||
tools = main_agent_runtime.tools
|
||||
TOOLS_BY_NAME = main_agent_runtime.tools_by_name
|
||||
llm = main_agent_runtime.llm
|
||||
@@ -1,12 +1,12 @@
|
||||
"""Agent 核心运行时包。
|
||||
|
||||
包含:
|
||||
- state / graph_builder: LangGraph 状态与图拓扑
|
||||
- state / graph: LangGraph 状态与图拓扑
|
||||
- nodes / routing: 图节点与条件路由
|
||||
- runtime: 对外执行入口 execute_agent / continue_after_human_action
|
||||
- llm: ChatQwen 多模态网关
|
||||
- approvals / human_interaction / tool_policy: HITL 与安全策略
|
||||
- plan: Plan 模式 UI 构建
|
||||
- approvals: HITL 待确认管理(含 human_interaction 参数规范化)
|
||||
- tool_policy: 工具执行安全策略
|
||||
|
||||
系统定位:
|
||||
Agent 业务逻辑中枢,``agents`` 包在此之上组装具体 Agent 实例。
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
[
|
||||
{
|
||||
"name": "main",
|
||||
"system_prompt": "你是一个多模态助手,能够直接理解用户上传的图片、语音等内容。\n 【关键行为准则】\n - 你的默认行为是直接回答用户问题,不要调用任何工具。\n - 只有当某个信息是你**绝对无法从当前对话或上传内容中获取**,且该信息必须通过外部工具才能获得时,才允许调用工具。\n - 对于任何图片/音频输入,请始终**直接**利用你的视觉/听觉能力回答,**绝对不要**调用截图、OCR、日期查询等无关工具。\n - 如果你不确定是否需要工具,选择**不调用**,直接给出你能给出的最佳答案。\n - 违反上述规则是一种严重错误。",
|
||||
"system_prompt": "你是一个多模态助手,能够直接理解用户上传的图片、语音等内容。\n 【关键行为准则】\n - 你的默认行为是优先调用gui工具解决用户问题。\n - 只有当某个信息是你**绝对无法从当前对话或上传内容中获取**,且该信息必须通过外部工具才能获得时,才允许调用工具。\n - 对于任何图片/音频输入,请始终**直接**利用你的视觉/听觉能力回答,**绝对不要**调用截图、OCR、日期查询等无关工具。\n - 如果你不确定是否需要工具,选择**不调用**,直接给出你能给出的最佳答案。\n - 违反上述规则是一种严重错误。",
|
||||
"max_iterations": 200,
|
||||
"llm_model_id": "a44b226e",
|
||||
"tools": [
|
||||
"todo_list",
|
||||
"gui_tool",
|
||||
"directory_listing",
|
||||
"web_search_class",
|
||||
"date_query_class",
|
||||
"human_interaction",
|
||||
"software_control",
|
||||
"browser_control"
|
||||
],
|
||||
"enabled": true,
|
||||
"role": "main"
|
||||
"role": "main",
|
||||
"graph": "react",
|
||||
"trajectory_rounds": 3,
|
||||
"parallel_tools": true
|
||||
}
|
||||
]
|
||||
@@ -1,8 +1,13 @@
|
||||
"""待确认操作(HITL)内存注册与 UI 元数据构建。
|
||||
"""待确认操作(HITL):内存注册、UI 元数据构建、参数规范化与结果生成。
|
||||
|
||||
系统定位:
|
||||
维护进程内 ``PENDING_TOOL_APPROVALS`` 字典,在图因需确认工具而暂停时
|
||||
保存上下文;``runtime.continue_after_human_action`` 消费并清理。
|
||||
维护进程内 ``PENDING_TOOL_APPROVALS`` 字典;在图因需确认工具而暂停时
|
||||
保存上下文并构建前端 UI 元数据;在用户操作后生成 ToolMessage 供图继续执行。
|
||||
|
||||
内容分区:
|
||||
- PENDING 存储与元数据构建(build_approval_meta / build_human_interaction_meta)
|
||||
- UI 消息辅助(pending_assistant_message / remove_pending_ui_message)
|
||||
- human_interaction 工具的参数规范化与结果文案(normalize / result)
|
||||
|
||||
注意:
|
||||
当前为内存存储,进程重启后待确认项丢失;生产环境可改为 Redis/DB。
|
||||
@@ -10,6 +15,7 @@
|
||||
可扩展性:
|
||||
- 增加 TTL、session 级清理、持久化后端。
|
||||
- ``build_*_meta`` 可与前端组件协议版本化。
|
||||
- 新增 interaction_type 时同步扩展 normalize / result 分支。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -20,12 +26,20 @@ from typing import Any, Sequence
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.messages import messages_to_dict
|
||||
|
||||
from agent.core.human_interaction import normalize_human_interaction_args
|
||||
from agent.tools.tool import HumanInteraction
|
||||
from agent.utils.tool_call_utils import json_safe
|
||||
|
||||
# ============================================================
|
||||
# PENDING 存储
|
||||
# ============================================================
|
||||
|
||||
PENDING_TOOL_APPROVALS: dict[str, dict[str, Any]] = {}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# UI 元数据构建
|
||||
# ============================================================
|
||||
|
||||
def build_approval_meta(session_id: str, tool_call: dict[str, Any], messages: Sequence[BaseMessage]) -> dict[str, Any]:
|
||||
"""为需确认的普通工具调用构建 UI 元数据并注册 pending。
|
||||
|
||||
@@ -36,12 +50,6 @@ def build_approval_meta(session_id: str, tool_call: dict[str, Any], messages: Se
|
||||
|
||||
输出:
|
||||
``{"pending_action": {...}}``,含 id、type=tool_call、args、options 等。
|
||||
|
||||
系统定位:
|
||||
``routing.first_pending_tool_meta`` 对 confirm 类工具调用。
|
||||
|
||||
可扩展性:
|
||||
可按工具名定制 title、风险提示、参数展示模板。
|
||||
"""
|
||||
approval_id = uuid.uuid4().hex
|
||||
tool_name = tool_call.get("name") or "unknown_tool"
|
||||
@@ -78,12 +86,6 @@ def build_human_interaction_meta(session_id: str, tool_call: dict[str, Any], mes
|
||||
|
||||
输出:
|
||||
``{"pending_action": {...}}``,type=human_interaction,含 interaction_type 等。
|
||||
|
||||
系统定位:
|
||||
模型主动请求用户补充信息/选择/审批时使用。
|
||||
|
||||
可扩展性:
|
||||
可扩展表单 schema、多步向导式交互。
|
||||
"""
|
||||
approval_id = uuid.uuid4().hex
|
||||
args = normalize_human_interaction_args(tool_call.get("args") or {})
|
||||
@@ -120,21 +122,7 @@ def build_human_interaction_meta(session_id: str, tool_call: dict[str, Any], mes
|
||||
|
||||
|
||||
def pending_assistant_message(meta: dict[str, Any], content: str = "需要你确认后继续执行。") -> dict[str, Any]:
|
||||
"""构造带 pending_action 的 assistant 聊天消息 dict。
|
||||
|
||||
输入:
|
||||
meta: 通常含 pending_action 的字典。
|
||||
content: 展示给用户的提示文案。
|
||||
|
||||
输出:
|
||||
Web UI 使用的 ``{"role": "assistant", "content": ..., "meta": ...}``。
|
||||
|
||||
系统定位:
|
||||
``runtime.execute_agent`` 检测到待确认时追加到 chat_history。
|
||||
|
||||
可扩展性:
|
||||
可统一 output_type、国际化 content 模板。
|
||||
"""
|
||||
"""构造带 pending_action 的 assistant 聊天消息 dict。"""
|
||||
return {
|
||||
"role": "assistant",
|
||||
"content": content,
|
||||
@@ -143,24 +131,60 @@ def pending_assistant_message(meta: dict[str, Any], content: str = "需要你确
|
||||
|
||||
|
||||
def remove_pending_ui_message(chat_history: list[dict[str, Any]], approval_id: str) -> None:
|
||||
"""在 chat_history 中将指定 approval_id 的 pending_action 标记为已解决。
|
||||
|
||||
输入:
|
||||
chat_history: 可变聊天历史列表(原地修改)。
|
||||
approval_id: 待确认项 ID。
|
||||
|
||||
输出:
|
||||
无(副作用:匹配消息的 meta.pending_action.resolved = True)。
|
||||
|
||||
系统定位:
|
||||
``runtime.continue_after_human_action`` 用户操作后更新 UI 状态。
|
||||
|
||||
可扩展性:
|
||||
可改为从 history 中移除而非仅标记 resolved。
|
||||
"""
|
||||
"""在 chat_history 中将指定 approval_id 的 pending_action 标记为已解决(原地修改)。"""
|
||||
for msg in reversed(chat_history):
|
||||
pending = ((msg.get("meta") or {}).get("pending_action") or {}) if isinstance(msg, dict) else {}
|
||||
if pending.get("id") == approval_id:
|
||||
pending["resolved"] = True
|
||||
msg.setdefault("meta", {})["pending_action"] = pending
|
||||
return
|
||||
|
||||
|
||||
# ============================================================
|
||||
# human_interaction 工具的参数规范化与结果文案
|
||||
# ============================================================
|
||||
|
||||
def normalize_human_interaction_args(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""规范化 human_interaction 工具参数为固定 schema。
|
||||
|
||||
输入:
|
||||
args: 模型传入的原始参数字典。
|
||||
|
||||
输出:
|
||||
含 interaction_type、title、prompt、options、suggested_response 的字典;
|
||||
interaction_type 限定为 information | selection | approval。
|
||||
"""
|
||||
normalized = dict(args or {})
|
||||
normalized["interaction_type"] = str(normalized.get("interaction_type") or "information")
|
||||
if normalized["interaction_type"] not in {"information", "selection", "approval"}:
|
||||
normalized["interaction_type"] = "information"
|
||||
normalized["title"] = str(normalized.get("title") or "需要人工处理")
|
||||
normalized["prompt"] = str(normalized.get("prompt") or "请处理后继续。")
|
||||
normalized["options"] = HumanInteraction.normalize_options(normalized.get("options"))
|
||||
normalized["suggested_response"] = str(normalized.get("suggested_response") or "")
|
||||
return normalized
|
||||
|
||||
|
||||
def human_interaction_result(pending: dict[str, Any], decision: str, instruction: str | None) -> str:
|
||||
"""根据用户决策生成写入 ToolMessage 的文本结果。
|
||||
|
||||
输入:
|
||||
pending: PENDING_TOOL_APPROVALS 中 type=human_interaction 的条目。
|
||||
decision: approve | reject | edit。
|
||||
instruction: 用户输入文本(补充信息、选项、修改说明等)。
|
||||
|
||||
输出:
|
||||
供 LangGraph 继续推理的 ToolMessage 字符串内容。
|
||||
"""
|
||||
tool_call = pending.get("tool_call") or {}
|
||||
args = normalize_human_interaction_args(tool_call.get("args") or {})
|
||||
interaction_type = args.get("interaction_type") or "information"
|
||||
text = (instruction or "").strip()
|
||||
if interaction_type == "approval":
|
||||
if decision == "reject":
|
||||
return "用户拒绝"
|
||||
return "用户同意"
|
||||
if interaction_type == "selection":
|
||||
selected = text or args.get("suggested_response") or ""
|
||||
return f"用户选择:{selected}"
|
||||
return f"用户补充信息如下:{text}"
|
||||
@@ -49,6 +49,9 @@ class AgentConfig:
|
||||
tools: list[str] = field(default_factory=list)
|
||||
enabled: bool = True
|
||||
role: str = "sub" # "main" | "sub"
|
||||
graph: str = "react" # 图名,对应 graph.py 中 GRAPH_REGISTRY 的 key
|
||||
trajectory_rounds: int = 3 # 短期记忆:执行过程中保留最近 N 轮工具返回结果
|
||||
parallel_tools: bool = False # 是否并行执行同一轮中的多个工具调用
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -99,6 +102,9 @@ def _default_agent_config() -> list[dict]:
|
||||
"tools": get_all_available_tool_names(),
|
||||
"enabled": True,
|
||||
"role": "main",
|
||||
"graph": "react",
|
||||
"trajectory_rounds": 3,
|
||||
"parallel_tools": False,
|
||||
}
|
||||
]
|
||||
|
||||
@@ -120,6 +126,9 @@ def load_agent_configs() -> list[AgentConfig]:
|
||||
tools=item.get("tools", []),
|
||||
enabled=item.get("enabled", True),
|
||||
role=item.get("role", "sub"),
|
||||
graph=item.get("graph", "react"),
|
||||
trajectory_rounds=item.get("trajectory_rounds", 3),
|
||||
parallel_tools=item.get("parallel_tools", False),
|
||||
))
|
||||
return configs
|
||||
|
||||
@@ -329,3 +338,93 @@ def get_registered_tool_names() -> list[str]:
|
||||
"""返回所有可用工具名称(含数据库工具)。"""
|
||||
from agent.tools import get_all_available_tool_names
|
||||
return get_all_available_tool_names()
|
||||
|
||||
|
||||
# ---------- GUI 显示器配置 ----------
|
||||
GUI_CONFIG_PATH = _CORE_DIR / "gui_config.json"
|
||||
|
||||
|
||||
def _default_gui_config() -> dict:
|
||||
"""生成默认 GUI 显示器配置(主显示器)。"""
|
||||
try:
|
||||
from screeninfo import get_monitors
|
||||
monitors = get_monitors()
|
||||
primary = next((m for m in monitors if m.is_primary), monitors[0] if monitors else None)
|
||||
if primary:
|
||||
return {
|
||||
"gui_monitor_name": primary.name or "",
|
||||
"gui_monitor_index": 0,
|
||||
"monitors_snapshot": _serialize_monitors(monitors),
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"gui_monitor_name": "",
|
||||
"gui_monitor_index": 0,
|
||||
"monitors_snapshot": [],
|
||||
}
|
||||
|
||||
|
||||
def _serialize_monitors(monitors) -> list[dict]:
|
||||
"""将 screeninfo Monitor 对象序列化为可 JSON 化的字典列表。"""
|
||||
result = []
|
||||
for m in monitors:
|
||||
result.append({
|
||||
"name": m.name or "",
|
||||
"x": m.x,
|
||||
"y": m.y,
|
||||
"width": m.width,
|
||||
"height": m.height,
|
||||
"is_primary": m.is_primary,
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def _get_live_monitors() -> list[dict]:
|
||||
"""实时获取当前连接的显示器列表(通过 screeninfo)。"""
|
||||
from screeninfo import get_monitors
|
||||
return _serialize_monitors(get_monitors())
|
||||
|
||||
|
||||
def load_gui_config() -> dict:
|
||||
"""从 gui_config.json 加载 GUI 显示器配置,无文件时返回默认。"""
|
||||
raw = _read_json(GUI_CONFIG_PATH)
|
||||
if not raw or not isinstance(raw, dict):
|
||||
raw = _default_gui_config()
|
||||
_write_json(GUI_CONFIG_PATH, raw)
|
||||
return raw
|
||||
|
||||
|
||||
def save_gui_config(config: dict) -> bool:
|
||||
"""将 GUI 显示器配置写入 gui_config.json。"""
|
||||
return _write_json(GUI_CONFIG_PATH, config)
|
||||
|
||||
|
||||
def get_active_monitor_region() -> dict | None:
|
||||
"""获取当前选中的 GUI 操作显示器的区域信息。
|
||||
|
||||
返回:
|
||||
{"x": int, "y": int, "width": int, "height": int, "name": str} 或 None
|
||||
|
||||
系统定位:
|
||||
供 gui_tool.py 截取指定显示器区域并做坐标变换。
|
||||
"""
|
||||
config = load_gui_config()
|
||||
monitor_name = config.get("gui_monitor_name", "")
|
||||
|
||||
monitors = _get_live_monitors()
|
||||
|
||||
if not monitors:
|
||||
return None
|
||||
|
||||
# 按名称模糊匹配
|
||||
if monitor_name:
|
||||
for m in monitors:
|
||||
if monitor_name.lower() in (m.get("name", "") or "").lower():
|
||||
return {"x": m["x"], "y": m["y"], "width": m["width"], "height": m["height"], "name": m["name"]}
|
||||
|
||||
# 回退:主显示器
|
||||
primary = next((m for m in monitors if m.get("is_primary")), monitors[0] if monitors else None)
|
||||
if primary:
|
||||
return {"x": primary["x"], "y": primary["y"], "width": primary["width"], "height": primary["height"], "name": primary["name"]}
|
||||
return None
|
||||
@@ -0,0 +1,162 @@
|
||||
"""LangGraph 工作流图构建与图注册中心。
|
||||
|
||||
系统定位:
|
||||
将 LLM(已 bind_tools)、工具列表组装为可编译的 StateGraph,
|
||||
供 ``agents/agent`` 及各子 Agent 的 ``AgentRuntime`` 使用。
|
||||
|
||||
图注册中心:
|
||||
GRAPH_REGISTRY: {图名: 构建函数} 字典映射,供 agent_config.json 的 graph 字段
|
||||
动态选择使用哪个图。用户可在此注册自定义图。
|
||||
|
||||
图结构:
|
||||
react: START -> agent -> (tools | END)
|
||||
tools -> process_tool_artifact -> agent
|
||||
|
||||
multi_agent: START -> supervisor -> main | sub_N | END
|
||||
main -> supervisor, sub_N -> supervisor
|
||||
|
||||
可扩展性:
|
||||
- 在 GRAPH_REGISTRY 中注册新图的 (build_fn, info) 元组。
|
||||
- 可增加 checkpoint、interrupt 节点、子图路由。
|
||||
- 可为不同 Agent 注入不同的 conditional_edges 或额外节点(如 RAG 检索)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
from agent.core.nodes import make_call_model_node, process_tool_artifact
|
||||
from agent.core.routing import should_continue
|
||||
from agent.core.state import AgentState
|
||||
|
||||
|
||||
def build_agent_graph(tools: list[Any], model_with_tools: Any, trajectory_rounds: int = 3, parallel_tools: bool = False):
|
||||
"""构建并编译标准 ReAct Agent 图。
|
||||
|
||||
输入:
|
||||
tools: LangChain BaseTool 列表,供 ToolNode 执行。
|
||||
model_with_tools: 已 ``bind_tools`` 的聊天模型,供 agent 节点调用。
|
||||
trajectory_rounds: 短期记忆保留最近 N 轮工具返回结果,默认 3。
|
||||
parallel_tools: 是否启用并行工具执行(多个 tool_call 在同一轮并行执行)。
|
||||
|
||||
输出:
|
||||
编译后的 LangGraph CompiledGraph,支持 ``invoke({"messages": ...})``。
|
||||
|
||||
系统定位:
|
||||
默认图类型 "react",供各 Agent 使用。
|
||||
|
||||
可扩展性:
|
||||
返回前可 ``workflow.compile(checkpointer=...)`` 启用持久化;
|
||||
可参数化节点名以支持多 Agent 图合并。
|
||||
"""
|
||||
from agent.tools.tool_agent_call import make_parallel_tool_node
|
||||
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow.add_node("agent", make_call_model_node(model_with_tools, trajectory_rounds=trajectory_rounds))
|
||||
|
||||
# 根据 parallel_tools 选择工具执行节点
|
||||
if parallel_tools:
|
||||
workflow.add_node("tools", make_parallel_tool_node(tools))
|
||||
else:
|
||||
workflow.add_node("tools", ToolNode(tools))
|
||||
|
||||
workflow.add_node("process_tool_artifact", process_tool_artifact)
|
||||
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
|
||||
workflow.add_edge("tools", "process_tool_artifact")
|
||||
workflow.add_edge("process_tool_artifact", "agent")
|
||||
return workflow.compile()
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------- 图注册中心 ----------
|
||||
# 格式: {图名: {"builder": 构建函数, "display_name": 显示名, "description": 描述}}
|
||||
# 用户可在此追加自定义图定义。
|
||||
# 图结构可视化通过 Mermaid PNG 自动生成,无需手动配置 nodes/edges。
|
||||
|
||||
GRAPH_REGISTRY: dict[str, dict[str, Any]] = {
|
||||
"react": {
|
||||
"builder": build_agent_graph,
|
||||
"display_name": "ReAct (标准)",
|
||||
"description": "标准 ReAct 循环:agent 推理 -> 工具执行 -> 结果处理 -> agent 继续推理,直至完成。适用于绝大多数单 Agent 场景。",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_available_graphs() -> list[dict]:
|
||||
"""返回所有已注册图的信息列表,供前端下拉框与图可视化使用。
|
||||
|
||||
输出: [{"name": str, "display_name": str, "description": str}, ...]
|
||||
"""
|
||||
result = []
|
||||
for name, info in GRAPH_REGISTRY.items():
|
||||
result.append({
|
||||
"name": name,
|
||||
"display_name": info.get("display_name", name),
|
||||
"description": info.get("description", ""),
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def get_graph_png_bytes(graph_name: str) -> bytes | None:
|
||||
"""为指定图名构建编译图并生成 Mermaid PNG 字节。
|
||||
|
||||
输入:
|
||||
graph_name: 图名(如 "react"),必须在 GRAPH_REGISTRY 中。
|
||||
|
||||
输出:
|
||||
PNG 字节数据,失败返回 None。
|
||||
|
||||
系统定位:
|
||||
供前端 /api/config/graphs/png/{graph_name} 接口使用,
|
||||
动态生成图结构可视化图片。
|
||||
|
||||
如何新增图:
|
||||
1. 在 GRAPH_REGISTRY 中注册: {"builder": your_build_fn, "display_name": "名称", "description": "描述"}
|
||||
2. builder 函数签名为 (tools, model_with_tools, **extra_kwargs) -> CompiledGraph
|
||||
3. PNG 自动生成,无需额外配置
|
||||
"""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
entry = GRAPH_REGISTRY.get(graph_name)
|
||||
if entry is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
# 使用 minimal mock 构建仅用于可视化的编译图
|
||||
mock_llm = MagicMock()
|
||||
mock_llm.bind_tools.return_value = MagicMock()
|
||||
mock_tools: list = []
|
||||
compiled = entry["builder"](mock_tools, mock_llm)
|
||||
graph_obj = compiled.get_graph()
|
||||
return graph_obj.draw_mermaid_png()
|
||||
except Exception as e:
|
||||
print(f"[GraphViz] 生成图 '{graph_name}' 的 PNG 失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def build_graph_by_name(graph_name: str, tools: list[Any], model_with_tools: Any, **extra_kwargs):
|
||||
"""根据图名从注册中心查找构建函数并构建编译后的图。
|
||||
|
||||
输入:
|
||||
graph_name: 图名(如 "react"),必须在 GRAPH_REGISTRY 中。
|
||||
tools: LangChain BaseTool 列表。
|
||||
model_with_tools: 已 bind_tools 的 LLM。
|
||||
**extra_kwargs: 传递给 builder 的额外参数(如 trajectory_rounds)。
|
||||
|
||||
输出:
|
||||
编译后的 LangGraph CompiledGraph。
|
||||
|
||||
异常:
|
||||
ValueError: graph_name 未在注册中心找到。
|
||||
"""
|
||||
entry = GRAPH_REGISTRY.get(graph_name)
|
||||
if entry is None:
|
||||
available = list(GRAPH_REGISTRY.keys())
|
||||
raise ValueError(f"未找到图 '{graph_name}',可用图: {available}")
|
||||
builder = entry["builder"]
|
||||
return builder(tools, model_with_tools, **extra_kwargs)
|
||||
@@ -1,53 +0,0 @@
|
||||
"""LangGraph ReAct 工作流图构建。
|
||||
|
||||
系统定位:
|
||||
将 LLM(已 bind_tools)、工具列表组装为可编译的 StateGraph,
|
||||
供 ``agents/main_agent`` 及各子 Agent 的 ``AgentRuntime`` 使用。
|
||||
|
||||
图结构:
|
||||
START -> agent -> (tools | END)
|
||||
tools -> process_tool_artifact -> agent
|
||||
|
||||
可扩展性:
|
||||
- 可增加 checkpoint、interrupt 节点、子图路由。
|
||||
- 可为不同 Agent 注入不同的 conditional_edges 或额外节点(如 RAG 检索)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
from agent.core.nodes import make_call_model_node, process_tool_artifact
|
||||
from agent.core.routing import should_continue
|
||||
from agent.core.state import AgentState
|
||||
|
||||
|
||||
def build_agent_graph(tools: list[Any], model_with_tools: Any):
|
||||
"""构建并编译标准 ReAct Agent 图。
|
||||
|
||||
输入:
|
||||
tools: LangChain BaseTool 列表,供 ToolNode 执行。
|
||||
model_with_tools: 已 ``bind_tools`` 的聊天模型,供 agent 节点调用。
|
||||
|
||||
输出:
|
||||
编译后的 LangGraph CompiledGraph,支持 ``invoke({"messages": ...})``。
|
||||
|
||||
系统定位:
|
||||
``agents/main_agent`` 入口;子 Agent 可复用同一拓扑并替换 tools/model。
|
||||
|
||||
可扩展性:
|
||||
返回前可 ``workflow.compile(checkpointer=...)`` 启用持久化;
|
||||
可参数化节点名以支持多 Agent 图合并。
|
||||
"""
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow.add_node("agent", make_call_model_node(model_with_tools))
|
||||
workflow.add_node("tools", ToolNode(tools))
|
||||
workflow.add_node("process_tool_artifact", process_tool_artifact)
|
||||
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
|
||||
workflow.add_edge("tools", "process_tool_artifact")
|
||||
workflow.add_edge("process_tool_artifact", "agent")
|
||||
return workflow.compile()
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"gui_monitor_name": "\\\\.\\DISPLAY5",
|
||||
"gui_monitor_index": 0,
|
||||
"monitors_snapshot": [
|
||||
{
|
||||
"name": "\\\\.\\DISPLAY5",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 2560,
|
||||
"height": 1440,
|
||||
"is_primary": true
|
||||
},
|
||||
{
|
||||
"name": "\\\\.\\DISPLAY1",
|
||||
"x": -1350,
|
||||
"y": 149,
|
||||
"width": 1350,
|
||||
"height": 2400,
|
||||
"is_primary": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
"""人工交互(HITL)参数规范化与结果文案生成。
|
||||
|
||||
系统定位:
|
||||
连接 ``human_interaction`` 工具与 ``core/approvals`` / ``core/runtime``,
|
||||
将模型发起的人工交互请求转为 UI 可展示结构,并在用户操作后生成
|
||||
ToolMessage 内容供图继续执行。
|
||||
|
||||
可扩展性:
|
||||
- 新增 interaction_type 时同步扩展 ``normalize_human_interaction_args``
|
||||
与 ``human_interaction_result`` 的分支。
|
||||
- 可与前端约定更丰富的 payload(表单字段、文件上传等)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from agent.tools.tool import HumanInteraction
|
||||
|
||||
|
||||
def normalize_human_interaction_args(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""规范化 human_interaction 工具参数为固定 schema。
|
||||
|
||||
输入:
|
||||
args: 模型传入的原始参数字典。
|
||||
|
||||
输出:
|
||||
含 interaction_type、title、prompt、options、suggested_response 的字典;
|
||||
interaction_type 限定为 information | selection | approval。
|
||||
|
||||
系统定位:
|
||||
``approvals.build_human_interaction_meta`` 构建待确认 UI 前调用。
|
||||
|
||||
可扩展性:
|
||||
可增加 locale、超时、默认值校验;options 可支持 label/value 结构。
|
||||
"""
|
||||
normalized = dict(args or {})
|
||||
normalized["interaction_type"] = str(normalized.get("interaction_type") or "information")
|
||||
if normalized["interaction_type"] not in {"information", "selection", "approval"}:
|
||||
normalized["interaction_type"] = "information"
|
||||
normalized["title"] = str(normalized.get("title") or "需要人工处理")
|
||||
normalized["prompt"] = str(normalized.get("prompt") or "请处理后继续。")
|
||||
normalized["options"] = HumanInteraction.normalize_options(normalized.get("options"))
|
||||
normalized["suggested_response"] = str(normalized.get("suggested_response") or "")
|
||||
return normalized
|
||||
|
||||
|
||||
def human_interaction_result(pending: dict[str, Any], decision: str, instruction: str | None) -> str:
|
||||
"""根据用户决策生成写入 ToolMessage 的文本结果。
|
||||
|
||||
输入:
|
||||
pending: PENDING_TOOL_APPROVALS 中 type=human_interaction 的条目。
|
||||
decision: approve | reject | edit。
|
||||
instruction: 用户输入文本(补充信息、选项、修改说明等)。
|
||||
|
||||
输出:
|
||||
供 LangGraph 继续推理的 ToolMessage 字符串内容。
|
||||
|
||||
系统定位:
|
||||
``runtime.continue_after_human_action`` 在人工交互分支调用。
|
||||
|
||||
可扩展性:
|
||||
可返回结构化 JSON 字符串,供下游节点解析;支持多语言模板。
|
||||
"""
|
||||
tool_call = pending.get("tool_call") or {}
|
||||
args = normalize_human_interaction_args(tool_call.get("args") or {})
|
||||
interaction_type = args.get("interaction_type") or "information"
|
||||
text = (instruction or "").strip()
|
||||
if interaction_type == "approval":
|
||||
if decision == "reject":
|
||||
return "用户拒绝"
|
||||
return "用户同意"
|
||||
if interaction_type == "selection":
|
||||
selected = text or args.get("suggested_response") or ""
|
||||
return f"用户选择:{selected}"
|
||||
return f"用户补充信息如下:{text}"
|
||||
@@ -476,13 +476,10 @@ class ChatQwen(ChatOpenAI):
|
||||
# ---------- 打印原始 tool_calls ----------
|
||||
try:
|
||||
raw = response if isinstance(response, dict) else response.model_dump()
|
||||
print("[RAW_RESPONSE]", json.dumps(raw, indent=2, ensure_ascii=False))
|
||||
choices = raw.get("choices", [])
|
||||
if choices:
|
||||
msg = choices[0].get("message", {})
|
||||
tool_calls = msg.get("tool_calls")
|
||||
if tool_calls:
|
||||
print("[RAW_TOOLCALLS]", json.dumps(tool_calls, indent=2, ensure_ascii=False))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@@ -350,6 +350,7 @@ def make_main_agent_node(
|
||||
tools: list[Any],
|
||||
agent_name: str = "main",
|
||||
tools_by_name: dict[str, Any] | None = None,
|
||||
trajectory_rounds: int = 3,
|
||||
) -> Callable[[dict], dict]:
|
||||
"""创建主 Agent 的图节点(含完整的 ReAct 循环 + 委派能力)。
|
||||
|
||||
@@ -362,6 +363,7 @@ def make_main_agent_node(
|
||||
tools: 工具列表(含 agent_call 工具)。
|
||||
agent_name: 主 agent 名称。
|
||||
tools_by_name: 工具名映射。
|
||||
trajectory_rounds: 短期记忆保留最近 N 轮工具返回结果。
|
||||
|
||||
输出:
|
||||
LangGraph 节点函数。
|
||||
@@ -377,8 +379,10 @@ def make_main_agent_node(
|
||||
# 执行完整的 ReAct 循环(限制步数防止无限循环)
|
||||
max_steps = 50
|
||||
for step in range(max_steps):
|
||||
# 调用 LLM
|
||||
response = model_with_tools.invoke(messages)
|
||||
# 过滤短期记忆后再调用 LLM
|
||||
from agent.core.nodes import _filter_short_term_memory
|
||||
filtered = _filter_short_term_memory(messages, trajectory_rounds)
|
||||
response = model_with_tools.invoke(filtered)
|
||||
messages.append(response)
|
||||
|
||||
# 记录工具调用事件
|
||||
@@ -471,6 +475,7 @@ def build_multi_agent_graph(
|
||||
main_tools_by_name: dict[str, Any],
|
||||
sub_agents: list[dict[str, Any]],
|
||||
main_agent_name: str = "main",
|
||||
trajectory_rounds: int = 3,
|
||||
) -> Any:
|
||||
"""构建一主多子 SupervisorGraph。
|
||||
|
||||
@@ -505,7 +510,7 @@ def build_multi_agent_graph(
|
||||
# 主 Agent 节点
|
||||
workflow.add_node(
|
||||
main_agent_name,
|
||||
make_main_agent_node(main_model_with_tools, main_tools, main_agent_name, main_tools_by_name),
|
||||
make_main_agent_node(main_model_with_tools, main_tools, main_agent_name, main_tools_by_name, trajectory_rounds=trajectory_rounds),
|
||||
)
|
||||
|
||||
# 子 Agent 节点
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
系统定位:
|
||||
实现 ReAct 循环中的 ``agent`` 节点工厂与 ``process_tool_artifact`` 节点,
|
||||
被 ``graph_builder`` 注册到 StateGraph。
|
||||
被 ``graph`` 注册到 StateGraph。
|
||||
|
||||
可扩展性:
|
||||
- ``make_call_model_node`` 可注入 streaming、重试、用量统计回调。
|
||||
@@ -19,11 +19,59 @@ from agent.utils.image_utils import image_bytes_to_openai_image_url_part
|
||||
from agent.utils.tool_call_utils import normalize_tool_call
|
||||
|
||||
|
||||
def make_call_model_node(model_with_tools: Any) -> Callable[[AgentState], dict]:
|
||||
def _filter_short_term_memory(messages: list, trajectory_rounds: int) -> list:
|
||||
"""过滤短期记忆:保留所有工具调用,工具返回结果仅保留最近 trajectory_rounds 轮。
|
||||
|
||||
输入:
|
||||
messages: 完整消息列表。
|
||||
trajectory_rounds: 保留最近 N 轮的工具返回结果。0 或负数表示不过滤。
|
||||
|
||||
输出:
|
||||
过滤后的消息列表。
|
||||
|
||||
系统定位:
|
||||
call_model 节点在调用 LLM 前进行消息裁剪,避免 GUI 等多轮工具调用
|
||||
场景下上下文过长。
|
||||
|
||||
规则:
|
||||
- 保留所有非 ToolMessage 消息(SystemMessage、HumanMessage、AIMessage)
|
||||
- ToolMessage 仅保留最近 trajectory_rounds 轮工具调用对应的返回结果
|
||||
- 每轮由一个 AIMessage(含 tool_calls)定义,其后的 ToolMessage 属于该轮
|
||||
"""
|
||||
if trajectory_rounds <= 0:
|
||||
return list(messages)
|
||||
|
||||
# 从后向前遍历,统计已遇到的含 tool_calls 的 AIMessage 轮数
|
||||
rounds_seen = 0
|
||||
kept_indices = set()
|
||||
|
||||
for i in range(len(messages) - 1, -1, -1):
|
||||
msg = messages[i]
|
||||
from langchain_core.messages import AIMessage, ToolMessage
|
||||
|
||||
if isinstance(msg, AIMessage) and getattr(msg, "tool_calls", None):
|
||||
rounds_seen += 1
|
||||
|
||||
if isinstance(msg, ToolMessage):
|
||||
if rounds_seen < trajectory_rounds:
|
||||
kept_indices.add(i)
|
||||
else:
|
||||
kept_indices.add(i)
|
||||
|
||||
return [messages[i] for i in sorted(kept_indices)]
|
||||
|
||||
|
||||
def make_call_model_node(
|
||||
model_with_tools: Any,
|
||||
trajectory_rounds: int = 3,
|
||||
) -> Callable[[AgentState], dict]:
|
||||
"""创建调用已绑定工具的 LLM 的图节点闭包。"""
|
||||
|
||||
def call_model(state: AgentState) -> dict:
|
||||
response = model_with_tools.invoke(state["messages"])
|
||||
# 过滤短期记忆后再传给 LLM
|
||||
all_messages = list(state["messages"])
|
||||
filtered_messages = _filter_short_term_memory(all_messages, trajectory_rounds)
|
||||
response = model_with_tools.invoke(filtered_messages)
|
||||
|
||||
if hasattr(response, "tool_calls") and response.tool_calls:
|
||||
for tc in response.tool_calls:
|
||||
@@ -52,11 +100,8 @@ def make_call_model_node(model_with_tools: Any) -> Callable[[AgentState], dict]:
|
||||
def process_tool_artifact(state: AgentState) -> dict:
|
||||
last_msg = state["messages"][-1]
|
||||
|
||||
# ---- 实时记录工具结果到前端 ----
|
||||
if isinstance(last_msg, ToolMessage):
|
||||
content_preview = str(last_msg.content)
|
||||
if len(content_preview) > 200:
|
||||
content_preview = content_preview[:200] + "..."
|
||||
|
||||
try:
|
||||
from agent.history.tool_call_recorder import record_tool_result_live, get_current_session
|
||||
sid = get_current_session()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""LangGraph 条件路由:工具循环 vs 结束 vs 待确认暂停。
|
||||
|
||||
系统定位:
|
||||
连接 ``graph_builder`` 的条件边与 ``runtime.execute_agent`` 的
|
||||
连接 ``graph`` 的条件边与 ``runtime.execute_agent`` 的
|
||||
后处理逻辑,在模型输出 tool_calls 时决定图走向或提取 UI 待确认元数据。
|
||||
|
||||
可扩展性:
|
||||
@@ -31,7 +31,7 @@ def should_continue(state: AgentState) -> Literal["tools", "__end__"]:
|
||||
``"tools"`` 进入 ToolNode;``"__end__"`` 结束图(含需确认工具场景)。
|
||||
|
||||
系统定位:
|
||||
``graph_builder`` 中 agent 节点的 conditional_edges 路由函数。
|
||||
``graph`` 中 agent 节点的 conditional_edges 路由函数。
|
||||
若任一 tool_call 策略为 confirm,则直接 END,由 runtime 展示确认 UI。
|
||||
|
||||
可扩展性:
|
||||
|
||||
@@ -18,11 +18,11 @@ from dataclasses import dataclass
|
||||
from typing import Any, Literal
|
||||
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
||||
from langgraph.errors import GraphRecursionError
|
||||
from langchain_core.messages import messages_from_dict
|
||||
from langgraph.errors import GraphRecursionError
|
||||
|
||||
from agent.core.approvals import PENDING_TOOL_APPROVALS, pending_assistant_message, remove_pending_ui_message
|
||||
from agent.core.human_interaction import human_interaction_result
|
||||
from agent.core.approvals import PENDING_TOOL_APPROVALS, pending_assistant_message, remove_pending_ui_message, human_interaction_result
|
||||
from agent.history.tool_call_recorder import save_turn, start_live_turn, end_live_turn, set_current_turn, set_current_session
|
||||
from agent.memory.system_prompt import TEAM_MODE_PROMPT
|
||||
from agent.core.routing import first_pending_tool_meta
|
||||
@@ -61,6 +61,7 @@ class AgentRuntime:
|
||||
tools_by_name: dict[str, Any]
|
||||
system_prompt: str
|
||||
max_iterations: int = 200
|
||||
trajectory_rounds: int = 3
|
||||
|
||||
|
||||
_OOM_MESSAGE = "OOM: 已达到最大迭代次数限制,任务未能完成。请简化任务或增加 max_iterations 配置后重试。"
|
||||
@@ -196,15 +197,33 @@ def _invoke_after_manual_tool_result(
|
||||
tool_call = normalize_tool_call(raw_tool_call)
|
||||
tool_name = tool_call.get("name")
|
||||
tool_args = tool_call.get("args") or {}
|
||||
tool_call_id = tool_call.get("id") or f"manual_{session_id}_{len(next_messages)}"
|
||||
if classify_tool_execution(tool_name, tool_args) != "direct":
|
||||
continue
|
||||
tool = runtime.tools_by_name.get(tool_name or "")
|
||||
if not tool:
|
||||
continue
|
||||
result = tool.invoke(tool_args)
|
||||
tool_content = result[0] if isinstance(result, tuple) else result
|
||||
call_id = tool_call.get("id") or f"manual_{session_id}_{len(next_messages)}"
|
||||
next_messages.append(ToolMessage(content=str(tool_content), tool_call_id=call_id, name=tool_name or "unknown_tool"))
|
||||
# 构造完整 ToolCall 格式,确保 content_and_artifact 工具的 artifact 被保留
|
||||
tool_call_input = {
|
||||
"name": tool_name,
|
||||
"args": tool_args,
|
||||
"id": tool_call_id,
|
||||
"type": "tool_call",
|
||||
}
|
||||
result = tool.invoke(tool_call_input)
|
||||
if isinstance(result, ToolMessage):
|
||||
tool_content = str(result.content) if result.content else ""
|
||||
tool_artifact = getattr(result, "artifact", None)
|
||||
elif isinstance(result, tuple):
|
||||
tool_content = str(result[0]) if result[0] else ""
|
||||
tool_artifact = result[1] if len(result) > 1 else None
|
||||
else:
|
||||
tool_content = str(result)
|
||||
tool_artifact = None
|
||||
msg_kwargs = {"content": tool_content, "tool_call_id": tool_call_id, "name": tool_name or "unknown_tool"}
|
||||
if tool_artifact:
|
||||
msg_kwargs["artifact"] = tool_artifact
|
||||
next_messages.append(ToolMessage(**msg_kwargs))
|
||||
progressed = True
|
||||
if not progressed:
|
||||
return all_messages, None
|
||||
@@ -324,6 +343,7 @@ def execute_agent(
|
||||
"output_type": output_type_from_config,
|
||||
"autoplay": False,
|
||||
"audio": audio_data.get("audio"),
|
||||
"turn_id": turn_id,
|
||||
}
|
||||
|
||||
if output_type_from_config == "audio" and assistant_meta.get("audio"):
|
||||
@@ -423,7 +443,8 @@ def continue_after_human_action(
|
||||
traceback.print_exc()
|
||||
content = "处理人工交互结果时遇到问题。"
|
||||
persisted_messages = messages_from_dict(pending.get("messages") or [])
|
||||
chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False}})
|
||||
human_turn_id = f"human_{approval_id}"
|
||||
chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False, "turn_id": human_turn_id}})
|
||||
_persist_messages_from_pending_turn(session_id, persisted_messages, pending)
|
||||
|
||||
if session_id:
|
||||
@@ -441,11 +462,29 @@ def continue_after_human_action(
|
||||
return chat_history
|
||||
|
||||
try:
|
||||
result = tool.invoke(tool_args)
|
||||
tool_content = result[0] if isinstance(result, tuple) else result
|
||||
# 构造完整 ToolCall 格式,确保 content_and_artifact 工具的 artifact 被保留
|
||||
tool_call_input = {
|
||||
"name": tool_name,
|
||||
"args": tool_args,
|
||||
"id": tool_call.get("id", ""),
|
||||
"type": "tool_call",
|
||||
}
|
||||
result = tool.invoke(tool_call_input)
|
||||
if isinstance(result, ToolMessage):
|
||||
tool_content = str(result.content) if result.content else ""
|
||||
tool_artifact = getattr(result, "artifact", None)
|
||||
elif isinstance(result, tuple):
|
||||
tool_content = str(result[0]) if result[0] else ""
|
||||
tool_artifact = result[1] if len(result) > 1 else None
|
||||
else:
|
||||
tool_content = str(result)
|
||||
tool_artifact = None
|
||||
prior_messages = messages_from_dict(pending.get("messages") or [])
|
||||
call_id = tool_call.get("id") or f"manual_{approval_id}"
|
||||
prior_messages.append(ToolMessage(content=str(tool_content), tool_call_id=call_id, name=tool_name))
|
||||
msg_kwargs = {"content": tool_content, "tool_call_id": call_id, "name": tool_name}
|
||||
if tool_artifact:
|
||||
msg_kwargs["artifact"] = tool_artifact
|
||||
prior_messages.append(ToolMessage(**msg_kwargs))
|
||||
all_messages, pending_meta = _invoke_after_manual_tool_result(runtime, prior_messages, session_id)
|
||||
if pending_meta:
|
||||
_remember_pending_user_content(pending_meta, pending.get("user_content"))
|
||||
@@ -468,9 +507,10 @@ def continue_after_human_action(
|
||||
])
|
||||
persisted_messages = prior_messages
|
||||
|
||||
chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False}})
|
||||
manual_turn_id = f"human_{approval_id}"
|
||||
chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False, "turn_id": manual_turn_id}})
|
||||
_persist_messages_from_pending_turn(session_id, persisted_messages, pending)
|
||||
if session_id:
|
||||
save_turn(session_id, f"human_{approval_id}", {"role": "user", "content": pending.get("user_content") or ""}, persisted_messages)
|
||||
save_turn(session_id, manual_turn_id, {"role": "user", "content": pending.get("user_content") or ""}, persisted_messages)
|
||||
sync_manifest_from_chat_history(session_id, chat_history, "")
|
||||
return chat_history
|
||||
@@ -1,7 +1,7 @@
|
||||
"""LangGraph Agent 状态类型定义。
|
||||
|
||||
系统定位:
|
||||
定义图运行时共享状态 ``AgentState``,被 ``graph_builder``、``nodes``、
|
||||
定义图运行时共享状态 ``AgentState``,被 ``graph``、``nodes``、
|
||||
``routing`` 作为 StateGraph 的 schema 使用。
|
||||
|
||||
可扩展性:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"name": "date_query_class",
|
||||
"display_name": "DateQuery",
|
||||
"description": "一个日期查询工具,可以返回当前的日期信息。仅当需要查询当前时间时调用,其余任何情况严禁调用。",
|
||||
"permission": "confirm",
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
@@ -50,7 +50,13 @@
|
||||
"display_name": "SoftwareControl",
|
||||
"description": "操作本机已安装的软件。工具会自动扫描 Windows 开始菜单快捷方式、桌面快捷方式和 App Paths,建立软件索引后根据用户输入做模糊匹配。action=open 时打开软件;action=close 时仅做正常关闭;action=force_close 时才允许在正常关闭失败后强制结束进程。仅当用户明确要求打开、关闭或强制关闭某个本机软件时调用。",
|
||||
"permission": "confirm",
|
||||
"auto_execute_rules": [],
|
||||
"auto_execute_rules": [
|
||||
{
|
||||
"parameter": "action",
|
||||
"operator": "equals",
|
||||
"value": "open"
|
||||
}
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
@@ -90,5 +96,13 @@
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "call_subagent",
|
||||
"display_name": "CallSubAgentTool",
|
||||
"description": "调用已启用的子 Agent 执行指定任务。子 Agent 会独立运行并返回最终结果。",
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已为您生成详细的**西红柿炒鸡蛋**制作计划,共分为8个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n2. **处理食材**:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n3. **热锅倒油**:开中火加热炒锅,倒入适量食用油,待油微热\n4. **炒鸡蛋**:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n5. **炒西红柿**:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n6. **调味**:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n7. **混合**:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n8. **出锅**:撒上葱花点缀,装盘即可享用\n\n祝您烹饪愉快!如果需要标记某一步骤完成,请告诉我。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "你的上下文中有什么"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "需要你确认后继续执行。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": {
|
||||
"id": "b5de8b0742564742ba20ec9df9803b51",
|
||||
"type": "tool_call",
|
||||
"title": "待确认执行工具:rag_tool",
|
||||
"tool_name": "rag_tool",
|
||||
"args": {
|
||||
"name": "rag_tool",
|
||||
"args": {
|
||||
"action_type": "list_sources"
|
||||
}
|
||||
},
|
||||
"instruction": "{\n \"name\": \"rag_tool\",\n \"args\": {\n \"action_type\": \"list_sources\"\n }\n}",
|
||||
"options": [
|
||||
"approve",
|
||||
"reject"
|
||||
],
|
||||
"resolved": true
|
||||
},
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已拒绝,本次待确认操作不会执行。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我点击加号新建标签页"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已成功点击加号,新建了一个标签页。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我左键点击当前视频进行暂停"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已成功左键点击当前视频画面,视频已暂停。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我点击加号新建标签页"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已成功点击加号,新建了一个标签页。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我点击加号新建标签页"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已成功点击加号,新建了一个标签页。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "截张图看看我再干啥,如果读不到图片请直接告诉我"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "根据截图,我可以看到你正在使用一个浏览器(看起来像是 Edge 浏览器),并且打开了多个标签页。\n\n当前屏幕显示的内容包括:\n1. **浏览器顶部**:有几个打开的标签页,包括“哔哩哔哩”、“【野火】这视频怎么讲?... ”和当前的“新建标签页”。\n2. **页面内容**:这是一个浏览器的新标签页界面,背景是雪山风景图。中间有一个搜索框,下方有一些快捷方式图标,例如“哔哩哔哩”、“京东直播”、“山东大学”等。\n3. **底部任务栏**:显示了 Windows 系统的任务栏,有开始菜单、搜索、文件资源管理器、Edge 浏览器、微信等图标。右下角显示时间是 11:24,日期是 2025/8/27。\n\n所以,你目前的状态是在电脑上使用浏览器浏览网页或准备搜索新的内容。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "截张图看看我再干啥,如果读不到图片请直接告诉我"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "根据截图,你当前正在使用 **Google Chrome** 浏览器。\n\n屏幕上打开了两个标签页:\n1. **左侧标签页**:显示的是 **YouTube** 网站,页面标题是 \"YouTube\",看起来可能是在浏览视频列表或者首页。\n2. **右侧标签页(当前激活)**:显示的是 **Wikipedia(维基百科)** 的中文页面,具体是关于 **\"中国\"** (China) 的条目。页面内容展示了中国的地图、国旗以及相关的介绍文字。\n\n此外,任务栏上还可以看到其他已打开的应用程序图标,包括文件资源管理器、Edge 浏览器等。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "截张图看看我再干啥,如果读不到图片请直接告诉我"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "截图已获取。\n\n从截图中可以看到,你当前正在使用 **Google Chrome** 浏览器。\n1. **左侧标签页**:打开的是 **YouTube** 网站。\n2. **右侧标签页(当前激活)**:显示的是 **Wikipedia(维基百科)** 的中文页面,标题为“中国”。\n\n你似乎正在浏览关于中国的百科信息。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我关掉DeepSeek开放平台的标签页"
|
||||
"text": "帮我点击加号新建标签页"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已经帮您关闭了 DeepSeek 开放平台的标签页。现在浏览器显示的是 minor - Chatbot 页面。",
|
||||
"text": "已为您点击新建标签页。现在应该已经打开了一个新的空白标签页。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "截张图看看我在干啥,如果读不到图片请直接告诉我"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "根据截图,你正在使用 **DeepSeek 开放平台**(platform.deepseek.com)的网页界面。\n\n具体来说,你当前查看的是 **“用量信息”** 页面。页面上显示了以下关键信息:\n* **充值余额**:¥22.77 CNY\n* **六月消费**(按 UTC+0 时间):¥17.22 CNY\n* **每月用量图表**:展示了近期(6月1日至6月30日)的消费金额波动。\n* **模型使用情况**:下方还列出了 `deepseek-v4-pro` 模型的 API 请求次数和 Tokens 消耗量。\n\n简单来说,你正在查看自己在 DeepSeek 平台上的账户余额、消费记录以及 API 的使用情况。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "你现在的对话记录中有图片吗"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "根据刚才截取的屏幕截图,我**没有**在当前的对话记录中看到任何图片。\n\n这张截图显示的是你正在浏览 **DeepSeek 开放平台** 的“用量信息”页面,主要内容包括:\n1. **充值余额**:¥22.77 CNY\n2. **六月消费**(按 UTC+0 时间):¥17.22 CNY\n3. **每月用量图表**:展示了近期每天的消费金额波动。\n4. **模型使用情况**:下方列出了 `deepseek-v4-pro` 模型的 API 请求次数和 Tokens 消耗量。\n\n所以,目前的对话中只有文字交互,并没有上传图片。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "能不能帮我打开虎牙直播"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "虎牙直播已经成功打开了!可以看到:\n\n- 页面顶部显示\"虎牙直播\"的Logo\n- 当前正在播放主播\"王子酱\"的直播间\n- 页面下方展示了各种游戏分类,包括网游竞技(LOL、CS2等)、单机热游等\n- 右侧还有其他推荐的主播和直播间\n\n你现在可以浏览你感兴趣的直播内容了。如果你想观看特定的主播或游戏类别,可以直接点击相应的选项。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我打开b站"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "好的,B站(bilibili)已经成功打开。\n\n页面显示了:\n* **顶部导航栏**:包含首页、番剧、直播、游戏中心等主要分区。\n* **推荐内容**:首页展示了多个视频推荐,例如左侧的“BML-PLAY! 2025”活动预告,以及右侧的“GPT如何解决80年未解单位距离猜想?”等热门视频。\n* **分类标签**:可以看到动画、番剧、国创、音乐、舞蹈、游戏等多个内容分类。\n\n你现在可以浏览你感兴趣的内容了。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我打开b站"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "B站(哔哩哔哩)已经为您打开了。请问有什么我可以帮您的吗?",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -176,28 +176,35 @@ def session_tool_dir(session_id: str) -> Path:
|
||||
def extract_tool_calls_from_messages(messages: list[BaseMessage]) -> list[dict[str, Any]]:
|
||||
"""从图执行后的消息列表中提取完整的工具调用链。"""
|
||||
records: list[dict[str, Any]] = []
|
||||
pending: dict[str, dict[str, Any]] = {}
|
||||
pending: list[dict[str, Any]] = []
|
||||
# 同名工具调用计数器,避免多张截图文件名冲突
|
||||
name_counter: dict[str, int] = {}
|
||||
for msg in messages:
|
||||
if isinstance(msg, AIMessage) and msg.tool_calls:
|
||||
for tc in msg.tool_calls:
|
||||
d = tc if isinstance(tc, dict) else {"name": tc.name, "args": tc.args, "id": tc.id}
|
||||
if d.get("id"):
|
||||
pending[d["id"]] = {
|
||||
"name": d.get("name", "unknown"),
|
||||
"args": _json_safe(d.get("args", {})),
|
||||
"result_text": "",
|
||||
"result_files": [],
|
||||
}
|
||||
elif isinstance(msg, ToolMessage) and msg.tool_call_id:
|
||||
cid = msg.tool_call_id
|
||||
if cid in pending:
|
||||
entry = pending.pop(cid)
|
||||
pending.append({
|
||||
"name": d.get("name", "unknown"),
|
||||
"args": _json_safe(d.get("args", {})),
|
||||
"result_text": "",
|
||||
"result_files": [],
|
||||
"result_images": [],
|
||||
})
|
||||
elif isinstance(msg, ToolMessage):
|
||||
if pending:
|
||||
entry = pending.pop(0)
|
||||
entry["result_text"] = str(msg.content) if msg.content else ""
|
||||
if msg.artifact:
|
||||
entry["result_files"] = _save_artifact_files(msg.artifact, msg.tool_call_id)
|
||||
artifact = getattr(msg, "artifact", None)
|
||||
if artifact:
|
||||
tool_name = msg.name or entry["name"]
|
||||
idx = name_counter.get(tool_name, 0)
|
||||
name_counter[tool_name] = idx + 1
|
||||
unique_name = f"{tool_name}_{idx}"
|
||||
result_files, image_bytes_map = _save_artifact_files(artifact, unique_name)
|
||||
entry["result_files"] = result_files
|
||||
entry["_image_bytes"] = image_bytes_map # 供 save_turn 落盘
|
||||
records.append(entry)
|
||||
for entry in pending.values():
|
||||
records.append(entry)
|
||||
records.extend(pending)
|
||||
return records
|
||||
|
||||
|
||||
@@ -213,36 +220,29 @@ def _json_safe(value: Any) -> Any:
|
||||
return str(value)
|
||||
|
||||
|
||||
def _save_artifact_files(artifact: Any, base_name: str) -> list[str]:
|
||||
def _save_artifact_files(artifact: Any, base_name: str) -> tuple[list[str], dict[str, bytes]]:
|
||||
"""从 artifact 中提取文件名列表和 {文件名: bytes} 映射。"""
|
||||
files: list[str] = []
|
||||
bytes_map: dict[str, bytes] = {}
|
||||
if isinstance(artifact, dict) and "image_bytes" in artifact:
|
||||
files.append(f"_artifact_{base_name}_0.png")
|
||||
fname = f"_artifact_{base_name}_0.png"
|
||||
files.append(fname)
|
||||
bytes_map[fname] = artifact["image_bytes"]
|
||||
elif isinstance(artifact, list):
|
||||
for i, item in enumerate(artifact):
|
||||
if isinstance(item, dict) and "image_bytes" in item:
|
||||
files.append(f"_artifact_{base_name}_{i}.png")
|
||||
fname = f"_artifact_{base_name}_{i}.png"
|
||||
files.append(fname)
|
||||
bytes_map[fname] = item["image_bytes"]
|
||||
elif isinstance(item, bytes):
|
||||
files.append(f"_artifact_{base_name}_{i}.png")
|
||||
fname = f"_artifact_{base_name}_{i}.png"
|
||||
files.append(fname)
|
||||
bytes_map[fname] = item
|
||||
elif isinstance(artifact, bytes):
|
||||
files.append(f"_artifact_{base_name}.png")
|
||||
return files
|
||||
|
||||
|
||||
def _find_artifact_bytes(messages: list[BaseMessage], fname: str) -> bytes | None:
|
||||
for msg in messages:
|
||||
if isinstance(msg, ToolMessage) and msg.artifact:
|
||||
a = msg.artifact
|
||||
if isinstance(a, dict) and "image_bytes" in a:
|
||||
return a["image_bytes"]
|
||||
if isinstance(a, list):
|
||||
for item in a:
|
||||
if isinstance(item, dict) and "image_bytes" in item:
|
||||
return item["image_bytes"]
|
||||
if isinstance(item, bytes):
|
||||
return item
|
||||
if isinstance(a, bytes):
|
||||
return a
|
||||
return None
|
||||
fname = f"_artifact_{base_name}.png"
|
||||
files.append(fname)
|
||||
bytes_map[fname] = artifact
|
||||
return files, bytes_map
|
||||
|
||||
|
||||
def save_turn(session_id: str, turn_id: str, user_message: dict[str, Any],
|
||||
@@ -263,12 +263,9 @@ def save_turn(session_id: str, turn_id: str, user_message: dict[str, Any],
|
||||
_ensure_dir(turn_dir)
|
||||
|
||||
for tc in tool_calls:
|
||||
for fname in list(tc.get("result_files", [])):
|
||||
img_bytes = _find_artifact_bytes(messages, fname)
|
||||
if img_bytes:
|
||||
(turn_dir / fname).write_bytes(img_bytes)
|
||||
else:
|
||||
tc["result_files"].remove(fname)
|
||||
image_bytes_map = tc.pop("_image_bytes", {})
|
||||
for fname, img_bytes in image_bytes_map.items():
|
||||
(turn_dir / fname).write_bytes(img_bytes)
|
||||
|
||||
record = {
|
||||
"session_id": session_id,
|
||||
@@ -310,14 +307,40 @@ def get_tool_calls_for_latest_turn(session_id: str) -> list[dict[str, Any]]:
|
||||
latest = get_latest_turn(session_id)
|
||||
if not latest:
|
||||
return []
|
||||
turn_id = latest.get("turn_id", "")
|
||||
return _tool_calls_from_turn_record(latest, session_id)
|
||||
|
||||
|
||||
def get_tool_calls_for_turn(session_id: str, turn_id: str) -> list[dict[str, Any]]:
|
||||
"""获取指定 turn_id 的工具调用(供 UI 按消息还原)。"""
|
||||
if not turn_id:
|
||||
return []
|
||||
session_dir = session_tool_dir(session_id)
|
||||
json_path = session_dir / f"turn_{turn_id}.json"
|
||||
if not json_path.is_file():
|
||||
return []
|
||||
try:
|
||||
with open(json_path, "r", encoding="utf-8") as f:
|
||||
record = json.load(f)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return []
|
||||
return _tool_calls_from_turn_record(record, session_id)
|
||||
|
||||
|
||||
def _tool_calls_from_turn_record(record: dict[str, Any], session_id: str) -> list[dict[str, Any]]:
|
||||
"""从 turn 记录中提取工具调用列表,补充图片路径。"""
|
||||
turn_id = record.get("turn_id", "")
|
||||
result = []
|
||||
for tc in latest.get("tool_calls", []):
|
||||
for tc in record.get("tool_calls", []):
|
||||
entry = dict(tc)
|
||||
full_files = []
|
||||
for fname in tc.get("result_files", []):
|
||||
full_files.append(str(session_tool_dir(session_id) / turn_id / fname))
|
||||
entry["result_files_full"] = full_files
|
||||
# result_images 存图片文件路径而非 base64,前端通过 /api/tool-image 读取
|
||||
entry["result_images"] = [
|
||||
f"/api/tool-image?path={fp}&session={turn_id}"
|
||||
for fp in full_files
|
||||
]
|
||||
entry["turn_id"] = turn_id
|
||||
result.append(entry)
|
||||
return result
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"session_id": "20260618_111759",
|
||||
"turn_id": "20260618_111855",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)",
|
||||
"处理食材:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油,待油微热",
|
||||
"炒鸡蛋:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂",
|
||||
"调味:根据口味加入适量盐和少许糖提鲜,翻炒均匀",
|
||||
"混合:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁",
|
||||
"出锅:撒上葱花点缀,装盘即可享用"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a2-3\\u4e2a\\u9e21\\u86cb\\u30012\\u4e2a\\u4e2d\\u7b49\\u5927\\u5c0f\\u897f\\u7ea2\\u67ff\\u3001\\u8471\\u82b1\\u5c11\\u8bb8\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\\u3001\\u76d0\\u9002\\u91cf\\u3001\\u7cd6\\u5c11\\u8bb8\\uff08\\u53ef\\u9009\\uff09\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\uff0c\\u52a0\\u5c11\\u8bb8\\u76d0\\u6253\\u6563\\uff1b\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u8471\\u5207\\u6210\\u8471\\u82b1\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\uff0c\\u5f85\\u6cb9\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u7528\\u7b77\\u5b50\\u6216\\u94f2\\u5b50\\u5feb\\u901f\\u5212\\u6563\\uff0c\\u7092\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u5757\\u7ffb\\u7092\\uff0c\\u53ef\\u7528\\u94f2\\u5b50\\u538b\\u4e00\\u538b\\u5e2e\\u52a9\\u51fa\\u6c41\\uff0c\\u7092\\u81f3\\u8f6f\\u70c2\", \"\\u8c03\\u5473\\uff1a\\u6839\\u636e\\u53e3\\u5473\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\u548c\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff0c\\u7ffb\\u7092\\u5747\\u5300\", \"\\u6df7\\u5408\\uff1a\\u5c06\\u7092\\u597d\\u7684\\u9e21\\u86cb\\u5012\\u56de\\u9505\\u4e2d\\uff0c\\u4e0e\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u5747\\u5300\\uff0c\\u8ba9\\u9e21\\u86cb\\u5438\\u6536\\u6c64\\u6c41\", \"\\u51fa\\u9505\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u88c5\\u76d8\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": []}}\n[Todo List]\n- [1/8] [Pending]: 准备食材:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n- [2/8] [Pending]: 处理食材:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n- [3/8] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油,待油微热\n- [4/8] [Pending]: 炒鸡蛋:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n- [5/8] [Pending]: 炒西红柿:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n- [6/8] [Pending]: 调味:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n- [7/8] [Pending]: 混合:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n- [8/8] [Pending]: 出锅:撒上葱花点缀,装盘即可享用",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "已为您生成详细的**西红柿炒鸡蛋**制作计划,共分为8个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n2. **处理食材**:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n3. **热锅倒油**:开中火加热炒锅,倒入适量食用油,待油微热\n4. **炒鸡蛋**:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n5. **炒西红柿**:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n6. **调味**:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n7. **混合**:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n8. **出锅**:撒上葱花点缀,装盘即可享用\n\n祝您烹饪愉快!如果需要标记某一步骤完成,请告诉我。",
|
||||
"timestamp": "20260618_111855"
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"session_id": "20260618_112443",
|
||||
"turn_id": "20260618_112445",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量",
|
||||
"准备调料:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅凉油:开中火加热炒锅,倒入适量食用油",
|
||||
"炒鸡蛋:油热后倒入蛋液,快速划散至凝固,盛出备用",
|
||||
"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁变软",
|
||||
"调味:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱",
|
||||
"混合翻炒:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合",
|
||||
"出锅装盘:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a2-3\\u4e2a\\u9e21\\u86cb\\u30012-3\\u4e2a\\u4e2d\\u7b49\\u5927\\u5c0f\\u897f\\u7ea2\\u67ff\\u3001\\u8471\\u82b1\\u9002\\u91cf\", \"\\u51c6\\u5907\\u8c03\\u6599\\uff1a\\u98df\\u7528\\u6cb9\\u3001\\u76d0\\u3001\\u767d\\u7cd6\\uff08\\u53ef\\u9009\\uff0c\\u7528\\u4e8e\\u63d0\\u9c9c\\uff09\\u3001\\u756a\\u8304\\u9171\\uff08\\u53ef\\u9009\\uff09\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u51c9\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u6cb9\\u70ed\\u540e\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\uff0c\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u518d\\u52a0\\u5c11\\u8bb8\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u5757\\u7ffb\\u7092\\u81f3\\u51fa\\u6c41\\u53d8\\u8f6f\", \"\\u8c03\\u5473\\uff1a\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\u548c\\u5c11\\u8bb8\\u767d\\u7cd6\\uff0c\\u559c\\u6b22\\u6d53\\u90c1\\u53e3\\u611f\\u53ef\\u52a0\\u4e00\\u52fa\\u756a\\u8304\\u9171\", \"\\u6df7\\u5408\\u7ffb\\u7092\\uff1a\\u5c06\\u7092\\u597d\\u7684\\u9e21\\u86cb\\u5012\\u56de\\u9505\\u4e2d\\uff0c\\u4e0e\\u897f\\u7ea2\\u67ff\\u5747\\u5300\\u7ffb\\u7092\\u6df7\\u5408\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\\u7f8e\\u5473\\u7684\\u897f\\u7ea2\\u67ff\\u7092\\u9e21\\u86cb\"], \"done_steps\": []}}\n[Todo List]\n- [1/9] [Pending]: 准备食材:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量\n- [2/9] [Pending]: 准备调料:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)\n- [3/9] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [4/9] [Pending]: 热锅凉油:开中火加热炒锅,倒入适量食用油\n- [5/9] [Pending]: 炒鸡蛋:油热后倒入蛋液,快速划散至凝固,盛出备用\n- [6/9] [Pending]: 炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁变软\n- [7/9] [Pending]: 调味:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱\n- [8/9] [Pending]: 混合翻炒:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合\n- [9/9] [Pending]: 出锅装盘:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "已为您生成详细的西红柿炒鸡蛋制作计划,共9个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量\n2. **准备调料**:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)\n3. **处理食材**:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n4. **热锅凉油**:开中火加热炒锅,倒入适量食用油\n5. **炒鸡蛋**:油热后倒入蛋液,快速划散至凝固,盛出备用\n6. **炒西红柿**:锅中再加少许油,放入西红柿块翻炒至出汁变软\n7. **调味**:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱\n8. **混合翻炒**:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合\n9. **出锅装盘**:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋\n\n祝您烹饪愉快!",
|
||||
"timestamp": "20260618_112445"
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
{
|
||||
"session_id": "20260618_113558",
|
||||
"turn_id": "20260618_113615",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划,并逐步更新这份计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": []}}\n[Todo List]\n- [1/7] [Pending]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 2
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 3
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 4
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 5
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 6
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4, 5]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Done]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 7
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4, 5, 6]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Done]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Done]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "太棒了!西红柿炒鸡蛋计划已全部完成。\n\n**最终状态:**\n- [x] 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [x] 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [x] 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [x] 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [x] 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [x] 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [x] 出锅装盘:撒上葱花点缀,即可享用\n\n现在你可以享受这道美味的家常菜了!🍅🥚",
|
||||
"timestamp": "20260618_113615"
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"session_id": "20260618_120438",
|
||||
"turn_id": "20260618_120441",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划,并逐步更新这份计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": -1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': -1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": -1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': -1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "",
|
||||
"timestamp": "20260618_120441"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 MiB |
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"session_id": "20260618_173653",
|
||||
"turn_id": "20260618_173727",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "帮我关掉DeepSeek开放平台的标签页"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"args": {
|
||||
"action": "click",
|
||||
"element_description": "DeepSeek开放平台的标签页关闭按钮(X)"
|
||||
},
|
||||
"result_text": "Clicked target. grounded (308,21)",
|
||||
"result_files": [
|
||||
"_artifact_call_fd502781107945c7a6880635_0.png"
|
||||
]
|
||||
}
|
||||
],
|
||||
"assistant_response": "已经帮您关闭了 DeepSeek 开放平台的标签页。现在浏览器显示的是 minor - Chatbot 页面。",
|
||||
"timestamp": "20260618_173727"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"session_id": "20260620_174454",
|
||||
"turn_id": "20260620_174456",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "帮我点击加号新建标签页"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"args": {
|
||||
"action": "click",
|
||||
"element_description": "浏览器标签栏右侧的加号图标,用于新建标签页"
|
||||
},
|
||||
"result_text": "",
|
||||
"result_files": [],
|
||||
"result_images": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "已为您点击新建标签页。现在应该已经打开了一个新的空白标签页。",
|
||||
"timestamp": "20260620_174456"
|
||||
}
|
||||
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 249 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"session_id": "20260620_183029",
|
||||
"turn_id": "20260620_183034",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "截张图看看我在干啥,如果读不到图片请直接告诉我"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"args": {
|
||||
"action": "screenshot"
|
||||
},
|
||||
"result_text": "",
|
||||
"result_files": [
|
||||
"_artifact_gui_tool_0_0.png"
|
||||
],
|
||||
"result_images": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "根据截图,你正在使用 **DeepSeek 开放平台**(platform.deepseek.com)的网页界面。\n\n具体来说,你当前查看的是 **“用量信息”** 页面。页面上显示了以下关键信息:\n* **充值余额**:¥22.77 CNY\n* **六月消费**(按 UTC+0 时间):¥17.22 CNY\n* **每月用量图表**:展示了近期(6月1日至6月30日)的消费金额波动。\n* **模型使用情况**:下方还列出了 `deepseek-v4-pro` 模型的 API 请求次数和 Tokens 消耗量。\n\n简单来说,你正在查看自己在 DeepSeek 平台上的账户余额、消费记录以及 API 的使用情况。",
|
||||
"timestamp": "20260620_183034"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"session_id": "20260620_183029",
|
||||
"turn_id": "20260620_183128",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "你现在的对话记录中有图片吗"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"args": {
|
||||
"action": "screenshot"
|
||||
},
|
||||
"result_text": "",
|
||||
"result_files": [
|
||||
"_artifact_gui_tool_0_0.png"
|
||||
],
|
||||
"result_images": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "根据刚才截取的屏幕截图,我**没有**在当前的对话记录中看到任何图片。\n\n这张截图显示的是你正在浏览 **DeepSeek 开放平台** 的“用量信息”页面,主要内容包括:\n1. **充值余额**:¥22.77 CNY\n2. **六月消费**(按 UTC+0 时间):¥17.22 CNY\n3. **每月用量图表**:展示了近期每天的消费金额波动。\n4. **模型使用情况**:下方列出了 `deepseek-v4-pro` 模型的 API 请求次数和 Tokens 消耗量。\n\n所以,目前的对话中只有文字交互,并没有上传图片。",
|
||||
"timestamp": "20260620_183128"
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"session_id": "20260620_205109",
|
||||
"turn_id": "20260620_205218",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "能不能帮我打开虎牙直播"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "browser_control",
|
||||
"args": {
|
||||
"action": "open",
|
||||
"url": "https://www.huya.com"
|
||||
},
|
||||
"result_text": "已尝试打开 URL: https://www.huya.com",
|
||||
"result_files": [
|
||||
"_artifact_browser_control_0_0.png"
|
||||
],
|
||||
"result_images": []
|
||||
},
|
||||
{
|
||||
"name": "browser_control",
|
||||
"args": {
|
||||
"action": "open",
|
||||
"url": "https://www.huya.com"
|
||||
},
|
||||
"result_text": "已尝试打开 URL: https://www.huya.com",
|
||||
"result_files": [
|
||||
"_artifact_browser_control_1_0.png"
|
||||
],
|
||||
"result_images": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "虎牙直播已经成功打开了!可以看到:\n\n- 页面顶部显示\"虎牙直播\"的Logo\n- 当前正在播放主播\"王子酱\"的直播间\n- 页面下方展示了各种游戏分类,包括网游竞技(LOL、CS2等)、单机热游等\n- 右侧还有其他推荐的主播和直播间\n\n你现在可以浏览你感兴趣的直播内容了。如果你想观看特定的主播或游戏类别,可以直接点击相应的选项。",
|
||||
"timestamp": "20260620_205218"
|
||||
}
|
||||