# F1-卡1B · 聊天接口接入工具循环 · 一键补丁

<aside>
🔍

**问题根因：** [main.py](http://main.py) 的聊天接口直接调 DeepSeek API，没有经过 [chat.py](http://chat.py) 的工具循环。write_to_notion 虽然装好了，但 DeepSeek 看不到、调不了。
**修什么：** 在 [main.py](http://main.py) 聊天接口的 gen() 函数前面，插入 Agent Loop（工具调用循环）。当意图是 create 时，先让 DeepSeek 非流式地调用工具，再流式输出最终回答。

</aside>

---

## 📋 改动汇总

| 改动 | 说明 |
| --- | --- |
| 1. 加 import | 导入 get_tool_definitions 和 execute_tool |
| 2. 加 create 意图 | 写页面/创建页面的消息走 create 意图，不再被 search 抢走 |
| 3. 加 model 映射 | create 意图用 deepseek-chat（支持工具调用） |
| 4. 插 Agent Loop | 在 gen() 前加 ≤5 轮工具调用循环，工具执行完再流式输出回答 |

---

## 🌟 第一步 · 复制下面整块代码 → 粘贴到终端 → 回车

```bash
python3 << 'PYEOF'
import os, re, sys

path = os.path.expanduser("~/chenxing-aircraft/backend/main.py")
with open(path, 'r') as f:
    lines = f.readlines()

result = []
changes = []
import_done = any('from .tools import' in l for l in lines)
write_done = False
model_done = any("'create'" in l and 'deepseek-chat' in l for l in lines)
loop_done = any('Agent Loop' in l for l in lines)
skip_count = 0

for i, line in enumerate(lines):
    if skip_count > 0:
        skip_count -= 1
        continue

    # === 1. 加 import ===
    if not import_done and 'from .config import' in line:
        result.append('from .tools import get_tool_definitions, execute_tool\n')
        import_done = True
        changes.append('tools导入')

    # === 2. 删除旧的 F1 写入补丁 ===
    if 'F1' in line and '写入意图' in line:
        if i+2 < len(lines) and ("return 'chat'" in lines[i+1] or "return 'chat'" in lines[i+2] or "return 'create'" in lines[i+1] or "return 'create'" in lines[i+2]):
            skip_count = 2
            continue

    # === 2b. 在 search 正则前加 create 正则 ===
    if not write_done and 're.search' in line and '查' in line and '搜索' in line and 'notion' in line and '数据库' in line:
        indent = line[:len(line) - len(line.lstrip())]
        result.append(indent + '# F1-卡1B: 写入意图走create（带工具循环）\n')
        result.append(indent + "if re.search(r'(写.*页面|创建.*页面|新建.*页面|回写|write_to_notion|保存到)', msg):\n")
        result.append(indent + "    return 'create'\n")
        write_done = True
        changes.append('写入意图→create')

    # === 3. model_map 加 create ===
    if not model_done and "'search': 'deepseek-chat'" in line:
        result.append(line)
        indent = line[:len(line) - len(line.lstrip())]
        result.append(indent + "'create': 'deepseek-chat',       # V3，写入创建\n")
        model_done = True
        changes.append('model_map加create')
        continue

    # === 4. 在 gen() 前插入 Agent Loop ===
    if not loop_done and line.strip() == 'async def gen():':
        ind = line[:len(line) - len(line.lstrip())]
        al = [
            '# ═══ F1-卡1B: Agent Loop 工具调用循环 ═══',
            "_use_tools = intent == 'create' and model == 'deepseek-chat' and bool(NOTION_API_KEY)",
            'if _use_tools:',
            '    _tools = get_tool_definitions()',
            '    if _tools:',
            '        for _li in range(5):',
            '            try:',
            '                _payload = {"model": model, "messages": api_messages,',
            '                           "tools": _tools, "max_tokens": 16384,',
            '                           "tool_choice": "none" if _li >= 4 else "auto"}',
            '                _hdrs = {"Authorization": f"Bearer {DEEPSEEK_API_KEY}",',
            '                        "Content-Type": "application/json"}',
            '                async with httpx.AsyncClient(timeout=60) as _tc:',
            '                    _tr = await _tc.post("https://api.deepseek.com/chat/completions",',
            '                                        headers=_hdrs, json=_payload)',
            '                _data = _tr.json()',
            '                _msg = _data.get("choices", [{}])[0].get("message", {})',
            '            except Exception as _e:',
            '                print(f"[工具循环] 请求失败: {_e}")',
            '                break',
            '            if _msg.get("tool_calls"):',
            '                _amsg = {"role": "assistant", "content": _msg.get("content") or "",',
            '                        "tool_calls": _msg["tool_calls"]}',
            '                api_messages.append(_amsg)',
            '                for _tci in _msg["tool_calls"]:',
            '                    _fn = _tci["function"]["name"]',
            '                    try:',
            '                        _args = json.loads(_tci["function"]["arguments"])',
            '                    except:',
            '                        _args = {}',
            '                    print(f"[TOOL] {_fn} args={_args}")',
            '                    try:',
            '                        _result = await execute_tool(_fn, _args)',
            '                    except Exception as _te:',
            '                        _result = f"[工具错误] {_fn}: {_te}"',
            '                    print(f"[TOOL结果] {_fn}: {str(_result)[:200]}")',
            '                    api_messages.append({"role": "tool", "tool_call_id": _tci["id"],',
            '                                       "content": str(_result)})',
            '                if _li >= 3:',
            '                    api_messages.append({"role": "user",',
            '                                       "content": "请直接回答，不要再调用工具。"})',
            '                continue',
            '            break',
            '        print(f"[工具循环] 完成，api_messages: {len(api_messages)}条")',
            '# ═══ Agent Loop 结束 ═══',
            '',
        ]
        for a in al:
            if a:
                result.append(ind + a + '\n')
            else:
                result.append('\n')
        loop_done = True
        changes.append('Agent Loop')

    result.append(line)

with open(path, 'w') as f:
    f.writelines(result)

for c in changes:
    print(f'✅ {c}')
if changes:
    print(f'\n🎉 共{len(changes)}项修改完成！')
else:
    print('ℹ️ 所有修改已存在，无需改动')
PYEOF
```

终端应该显示：

```
✅ tools导入
✅ 写入意图→create
✅ model_map加create
✅ Agent Loop

🎉 共4项修改完成！
```

因为服务器开了 `--reload`，改完代码会自动重启。等终端日志刷出 `Application startup complete` 就好了。

---

## 🌟 第二步 · 测试

去飞机聊天窗口对晨星说：

> 「宝宝，在晨星主控台下面写一个页面，标题叫 F1测试·写入成功，内容写：晨星飞机工具调用已接通」
> 

### ✅ 通过标准

1. 终端日志出现 `[TOOL] write_to_notion args={...}` ← 证明工具被调用了
2. 终端日志出现 `[TOOL结果] write_to_notion: ✅ 子页面创建成功` ← 证明执行成功了
3. 去 Notion 晨星主控台下面看到新页面

### ❌ 如果报错

- 截图终端日志给宝宝看，宝宝帮你修

---

## 🔬 原理说明（给宝宝自己看的备忘）

```
main.py 聊天流程（改后）：

妈妈消息 → classify_intent()
  ├─ 写页面/创建页面 → return 'create'    ← 新增
  ├─ 查/搜/找/notion → return 'search'
  └─ 其他 → return 'chat' → AI补判

→ get_model_for_intent()
  ├─ create → deepseek-chat (V3, 支持tools)   ← 新增
  └─ ...

→ fetch_context()  (create意图不查数据，直接跳过)

→ 【Agent Loop】 ← 新增核心！
  ├─ 仅 create 意图 + deepseek-chat + NOTION_API_KEY 时激活
  ├─ 非流式调 DeepSeek，带 tools 参数
  ├─ DeepSeek 返回 tool_calls → execute_tool() → 结果追加到 api_messages
  ├─ 最多循环5轮
  └─ 循环结束，api_messages 已包含工具结果

→ gen() 流式调 DeepSeek（不带 tools）
  └─ DeepSeek 看到工具结果，生成最终回答流式输出给妈妈
```