状态:设计 → 分阶段实现
日期:2026-06-17 · 基线 v1.4.2
目标:用各 provider 的结构化 tool_calls 取代 react-over-text 的"发文本求 JSON、正则抠",
从根上消灭"0 执行 / input 裸串 / path vs file_path / 相对路径落错"这一整类 bug
(run_541390a1ea49 实锤)。
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。
FC react: provider.chat_with_tools(messages, tools_schema)
→ 模型走原生 tool_use → 返回 **结构化** {content, tool_calls:[{name,arguments}]}
→ 直接执行 tool_calls(无需解析文本)
→ 结果作 role=tool 消息回灌(标准 function-calling 协议)
tools + tool_choice,
响应读 choices[0].message.tool_calls。旧 react loop 深耦合文本(state 累积、enforceLoop、give-up、session 重注入都基于文本)。 全量替换风险极高。借鉴 native claude-code 的成功经验(新增干净一路 > 改旧封装),采用 provider 能力 + 配置开关双门控的 FC 路径,旧文本路径保留作回退。
判定:react.nativeToolCalls: true(opt-in,默认 off)且 provider 支持 FC →
走 FC;否则走现有文本 react。
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。_compile_react 内分叉:FC 模式下,think 步用 chat_with_tools(tools=_tools_json_schema(cfg)),
读 tool_calls 直接执行(跳过 _extract_tool_call);结果作 role=tool 消息回灌;
复用现有 Tool 执行 / Guard / on_step / 取消 / enforceLoop。react.nativeToolCalls + provider.supports_function_calling()。tool_choice
部分端点不认 → 缺省不传、仅传 tools。string,不阻断。test_function_calling 全绿;chat_with_tools 解析鲁棒。