> ## Documentation Index
> Fetch the complete documentation index at: https://qitor.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 第 3 课：Claude Code 风格智能体

> 构建一个长时间运行的编码智能体，并理解何时从手工注册表转向预设、历史控制与上下文压缩。

这一课开始，课程会从模式设计真正走向操作工作流。

你仍在使用同一个 QitOS 执行内核，但智能体现在必须在工作区中稳定运行很多步。这就带来了新的设计问题：

* 还要继续手工组装工具界面吗？
* 系统提示词里应该放多少工作流纪律？
* 什么时候 `HistoryPolicy` 就够了？
* 什么时候应该升级到 `CompactHistory` 或显式记忆？

本课对应的示例是 `examples/real/claude_code_agent.py`。

## 相比第 2 课的变化

| 设计分支  | 第 2 课                       | 第 3 课                                                |
| ----- | --------------------------- | ---------------------------------------------------- |
| 工具    | 手工注册表 + 裁剪版 `CodingToolSet` | `coding_tools(...)` 预设注册表                            |
| 提示词   | 规划器 + 执行器提示词                | 单个重工作流系统提示词                                          |
| 状态    | 计划与游标                       | 待办事项、模式、目标文件、验证命令、可选文档链接                             |
| 历史    | 默认行为                        | 显式 `HistoryPolicy(max_messages=16, max_tokens=2800)` |
| 记忆    | 不使用                         | 默认仍不使用，但此时记忆开始成为真实选项                                 |
| 上下文压缩 | 还未引入                        | 作为更长运行的升级路径被正式引入                                     |

## 系统提示词现在定义的是工作流纪律

和第 1 课不同，这一课的提示词不再只是解析器契约，它还编码了操作流程：

```text theme={null}
Workflow:
- Start by writing a todo list with `todo_write`.
- If you are unsure which tool to use, call `tool_search`.
- Read before you edit.
- Make the smallest correct change.
- Run verification immediately after editing.
- 只有在任务确实需要文档时才使用 `web_fetch`。
```

在 v0.4 中，输出契约已经不再被硬编码在示例提示词里，而是由当前协议（模型输出的格式契约）自动注入：

```text theme={null}
Thought: <short reasoning>
Action: <tool_name>(arg=value, ...)
or
Final Answer: <what changed + verification proof>
```

这一课想让你看到：

* 运行时没变
* 提示词却可以变得非常面向操作

## 本课默认解析器依旧故意保持 ReAct

尽管提示词复杂了很多，解析器仍然是：

```python theme={null}
model_parser=ReActTextParser()
```

这是有意为之的。你正在学习：仅通过

* 更合理的 state
* 更成熟的工具界面
* 更清晰的工作流提示词

就可以显著改变智能体行为，而不必立刻升级协议。

## 这个示例现在是预设优先的

底层传输层仍然可以是 `OpenAICompatibleModel`，但 v0.4 会先解析：

* `FamilyPreset`（预配置的模型家族默认值）
* `HarnessPolicy`
* 协议 / 解析器 / 工具交付方式 / 上下文默认值

因此，同一个示例现在可以在 Qwen、Kimi、MiniMax、`gpt-oss` 与 Gemma 4 之间切换，而不用改智能体实现本身。

对其中多数家族来说，适配层依然是文本或 JSON 优先的：

* 模型返回文本
* 工具模式通过提示词注入，或通过工具参数交付
* 解析器把文本解析成决策（智能体每步的结构化决策）

MiniMax 则继续保留自己更适合的工具调用解析器。

这种路径对研究仍然很有价值，因为它：

* 容易跨供应商比较
* 容易在追踪记录中检查
* 容易接到本地端点

如果你确实需要家族专属协议，QitOS 现在会通过预设系统把这类耦合显式记录下来。

<Steps>
  <Step title="从预设工具注册表开始">
    本课通常会这样初始化：

    ```python theme={null}
    super().__init__(
        toolset=[
            coding_tools(
                workspace_root=workspace_root,
                shell_timeout=30,
                include_notebook=True,
            )
        ],
        llm=llm,
        model_parser=ReActTextParser(),
    )
    ```

    到了这里，预设才是正确的抽象层。

    `coding_tools(...)` 直接给你一个一致的工作区捆绑包，不必再手工把文件、终端、任务、笔记本工具逐个注册。

    这一课的经验是：

    * 学习内核时手工暴露工具
    * 智能体进入真实工作流后切换到预设
  </Step>

  <Step title="理解预设真正带来了什么">
    `coding_tools(...)` 是 QitOS 的标准编码工具包。

    实际上它通常意味着智能体拥有：

    * 文件查看与编辑
    * 终端执行
    * 任务与待办辅助
    * 可选笔记本支持
    * 可选网页与文档工具

    当智能体走到这一步时，你选择的不再是一个工具，而是在选择一个工作环境。
  </Step>

  <Step title="把 state 设计成长时运行友好的形状">
    这一课的 state 会开始承载工作流信号：

    ```python theme={null}
    @dataclass
    class ClaudeCodeState(StateSchema):
        scratchpad: list[str] = field(default_factory=list)
        todos: list[dict[str, Any]] = field(default_factory=list)
        target_file: str = TARGET_FILE
        test_command: str = TEST_COMMAND
        doc_url: str = DOC_URL
        mode: str = "work"
    ```

    这套 state 有效，是因为：

    * `todos` 把工作队列显式化，并能跨多步保留
    * `mode` 帮助智能体记住自己处于规划还是执行阶段
    * `doc_url` 让外部文档检索变成可选输入，而不是默认浏览
    * `scratchpad` 仍保存压缩后的最近轨迹
  </Step>

  <Step title="让归约吸收结构化工具输出">
    `reduce()` 会开始从工具结果中吸收结构化工作流状态：

    ```python theme={null}
    if isinstance(first, dict):
        if first.get("todos"):
            state.todos = list(first.get("todos") or [])
        if first.get("current_mode"):
            state.mode = str(first.get("current_mode"))
        if int(first.get("returncode", 1)) == 0:
            state.final_result = (
                "Verification passed with the canonical coding toolset."
            )
    if state.metadata.get("todos"):
        state.todos = list(state.metadata.get("todos") or [])
    if state.metadata.get("mode"):
        state.mode = str(state.metadata.get("mode"))
    ```

    这仍然是在延续前两课的同一原则：

    **工具在做工作，但真正决定智能体该记住什么的，依然是 `reduce()`。**
  </Step>

  <Step title="第一次显式引入历史控制">
    本课的运行通常会传：

    ```python theme={null}
    history_policy=HistoryPolicy(max_messages=16, max_tokens=2800)
    ```

    这是课程里第一次让消息窗口管理变得重要。

    `HistoryPolicy` 回答的是：

    * 最近多少消息会被保留
    * 历史最多可以占多少 token
    * 何时旧上下文不再原样进入模型
  </Step>

  <Step title="理解历史、上下文压缩与记忆的边界">
    在这一课里，示例默认仍然**不**挂载自定义 `history=` 或 `memory=`。

    这是一个有意为之的设计：

    * `HistoryPolicy` 负责消息预算
    * state 负责保存待办事项、模式之类的即时工作流产物
    * 还不需要单独的记忆存储

    但当运行足够长、简单裁剪已经开始影响行为时，就应该升级到 `CompactHistory`：

    ```python theme={null}
    from qitos.kit import CompactConfig, CompactHistory, WindowMemory

    super().__init__(
        toolset=[coding_tools(workspace_root=workspace_root)],
        llm=llm,
        model_parser=ReActTextParser(),
        history=CompactHistory(
            llm=llm,
            config=CompactConfig(
                max_tokens=2800,
                keep_last_messages=10,
                keep_last_rounds=4,
            ),
        ),
        memory=WindowMemory(window_size=30),
    )
    ```

    要把这几层读清楚：

    * `HistoryPolicy`：这次请求带哪些消息
    * `CompactHistory`：旧交互如何被上下文压缩压缩与保留
    * `Memory`：什么信息要脱离即时消息流而长期存在
  </Step>

  <Step title="理解什么时候值得升级协议">
    即使智能体更复杂了，本课依然继续使用文本 ReAct。这通常仍是正确选择。

    只有在这些场景下才值得考虑协议升级：

    * 需要比文本 ReAct 更严格的结构化输出时，升级到 JSON 或 XML
    * 智能体正在驱动实时终端会话时，考虑 Terminus
    * 供应商原生输出结构化工具调用且你确实想保留它时，使用 `MiniMaxToolCallParser` 等模型专属解析器

    **不要因为智能体变复杂了，就自动切换协议。**
  </Step>

  <Step title="像操作者一样运行，像研究者一样检查">
    运行：

    ```bash theme={null}
    python examples/real/claude_code_agent.py
    ```

    查看：

    ```bash theme={null}
    qita board --logdir runs
    ```

    在 `qita` 中重点看：

    * 待办事项是否在前期建立，并且后续保持一致
    * `mode` 的切换是否符合预期工作流
    * 提示词与解析器是否仍然保持在简单 ReAct 主路径上
    * 历史裁剪是否开始影响模型行为
    * 这次运行是否已经值得升级到 `CompactHistory`
  </Step>
</Steps>

## 长时运行智能体的正确心智模型

到这一课为止，你应该开始用分层的方式思考：

* state：下一步**一定**需要什么
* history：下一次模型调用**可能**需要什么
* 上下文压缩：更早的历史如何被压缩
* memory：哪些信息应该脱离即时轮次结构长期存在

这也是 QitOS 在长时运行设计上最重要的区分。

## 完整示例

完整可运行代码位于：

* [`examples/real/claude_code_agent.py`](https://github.com/Qitor/qitos/blob/main/examples/real/claude_code_agent.py)

## 第 4 课会引入什么

第 4 课会继续保留长时运行结构，但任务域会完全切换。

你将看到如何在**不发明新运行时**的前提下，调整：

* 工具组合
* 提示词策略
* 状态语义
* `reduce()` 逻辑

从而得到一个真正的领域化智能体。

<CardGroup cols={2}>
  <Card title="下一课：代码安全审计智能体" icon="shield-check" href="/zh/tutorials/code-security-audit">
    把同一个内核变成具备排序发现的防御式审查智能体
  </Card>

  <Card title="相关指南：可观测性" icon="chart-line" href="/zh/guides/observability">
    在进入最终课程前，先把 qita 面板、回放与导出再回顾一遍
  </Card>
</CardGroup>
