# 原生 function-calling 改造设计(评估建议 ①) 状态:设计 → 分阶段实现 日期:2026-06-17 · 基线 v1.4.2 目标:用各 provider 的**结构化 tool_calls** 取代 react-over-text 的"发文本求 JSON、正则抠", 从根上消灭"0 执行 / `input` 裸串 / `path` vs `file_path` / 相对路径落错"这一整类 bug (run_541390a1ea49 实锤)。 ## 1. 现状(为什么脆) ``` react loop: 构造文本 prompt("请输出一个 JSON 工具调用") → think.apply(text) → provider.chat(messages) → 返回 **纯 text** → _extract_tool_call(text) 正则/JSON 扫描抠 {"tool":...,"input":...} → 执行 → 把结果拼成文本 "[工具执行结果]..." 再喂回 ``` provider.chat 只发 `{model, messages, temperature, max_tokens}`,**不发 tools**,只返回 `content`。 模型把工具调用当散文手写 → 格式飘 → 解析失败/参数错。 > **codex 修正(2026-06-17)**: > - FC 只治"格式漂移 / JSON 抠错 / 裸串 input / 工具名解析"这一类;**"相对路径落错目录" > 不在其中**——那是 workspace contract + cwd 绑定 + sandbox + 参数 canonicalization 的 > 工具层硬约束(`file_tools._resolve`/`_sandbox`,已部分解决)。别把后者算进 FC 收益。 > - 战略上 **react-over-text 降级为 legacy/fallback,不再作为主车道**(不是与 FC 平级)。 > FC-capable provider 一律走 FC;native harness(claude-code/codex)负责 workspace agentic。 ## 2. 目标架构 ``` FC react: provider.chat_with_tools(messages, tools_schema) → 模型走原生 tool_use → 返回 **结构化** {content, tool_calls:[{name,arguments}]} → 直接执行 tool_calls(无需解析文本) → 结果作 role=tool 消息回灌(标准 function-calling 协议) ``` - OpenAI 兼容(qwen/deepseek/openai/moonshot/zhipu):请求加 `tools` + `tool_choice`, 响应读 `choices[0].message.tool_calls`。 - claude-code:已有 native 车道,不动。 - 不支持 FC 的模型(部分 ollama 旧模型):回退现有 react-over-text 文本解析。 ## 3. 关键决策:新增"FC 增强"而非重写旧 loop 旧 react loop 深耦合文本(state 累积、enforceLoop、give-up、session 重注入都基于文本)。 全量替换风险极高。借鉴 native claude-code 的成功经验(新增干净一路 > 改旧封装),采用 **provider 能力 + 配置开关双门控的 FC 路径**,旧文本路径保留作回退。 判定:`react.nativeToolCalls: true`(opt-in,默认 off)**且** provider 支持 FC → 走 FC;否则走现有文本 react。 ## 4. 分阶段实现(降风险、每阶段可独测) ### 阶段 1(本次):provider 基础层 + 工具 JSON schema(低风险、纯增量) - `providers/openai_compat_provider.py`: - 新增 `chat_with_tools(messages, tools, tool_choice="auto") -> dict`,返回 `{"content": str|None, "tool_calls": [{"id","name","arguments"(dict)}], "usage": {...}}`。 复用现有 HTTP/错误处理(含 `_is_transient_net_error` 重试、HTTPError)。 - `supports_function_calling()` → True(OpenAI 兼容默认支持;ollama 按模型)。 - `fromconfig/compiler.py`:新增 `_tools_json_schema(cfg) -> list`,复用 `_generate_tool_schema_docs` 的签名内省,产出 OpenAI tools 规格 `[{"type":"function","function":{"name","description","parameters":{JSON schema}}}]`。 参数类型从 schema 类 `__init__` 注解推断(str/int/bool…),缺省→optional。 - 单测 `test_function_calling`: - `_tools_json_schema` 对 ReadFile/WriteFile/Bash 产出正确 name/required; - `chat_with_tools` 用 mock HTTP 返回带 tool_calls 的响应 → 正确解析成 `{name, arguments(dict)}`;返回 content-only 时 tool_calls=[];arguments 是字符串 JSON 时能 parse。 - **不动 react loop** → 零回归风险。交付一个被测过的可靠积木。 ### 阶段 2(已完成,live 验证):FC react 执行 + 路由 - `_compile_react` 内分叉:FC 模式下,think 步用 `chat_with_tools(tools=_tools_json_schema(cfg))`, 读 `tool_calls` 直接执行(跳过 `_extract_tool_call`);结果作 role=tool 消息回灌; 复用现有 Tool 执行 / Guard / on_step / 取消 / enforceLoop。 - ConversationLam:FC 模式消息序列含 assistant(tool_calls) + tool(result)。 - 路由门控:`react.nativeToolCalls` + `provider.supports_function_calling()`。 - 先在 qwen-plus 上 live 验证:同批任务工具执行成功率/落盘率对比文本路径。 ### 阶段 3(评估②联动):用 eval 量化收益 - 复用 docs/AGENT_QUALITY_ASSESSMENT.md ② 的 eval 闭环,对比 FC vs 文本路径的 落盘率/达标率/步数/成本,数据驱动决定是否把 FC 设为默认。 ## 5. 风险与缓解 - **旧 loop 不动**(阶段 1)→ 无回归;阶段 2 的 FC 路径 opt-in、可一键退回文本路径。 - **provider 兼容差异**:dashscope 的 tools 字段与 OpenAI 基本一致;ollama 视模型。`tool_choice` 部分端点不认 → 缺省不传、仅传 tools。 - **arguments 可能是字符串**(某些端点把 arguments 序列化成 JSON 字符串)→ 解析层兼容 dict/str。 - **参数 schema 不全**:内省拿不到类型时退化为 `string`,不阻断。 ## 6. 验收 - 阶段 1:`test_function_calling` 全绿;`chat_with_tools` 解析鲁棒。 - 阶段 2:qwen-plus FC 路径 live 跑通一条多步任务,工具真执行、产物落盘、0 个"格式飘"错。 - 整体:FC 路径下 run_541390a1ea49 类(0 执行)不再出现。