Skip to content

Commit 34cfa37

Browse files
Feat : 对齐Claude Code 的Todo Write能力
1 parent c40b281 commit 34cfa37

9 files changed

Lines changed: 1013 additions & 0 deletions

File tree

examples/todo_tool/README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# TodoWriteTool 任务清单示例
2+
3+
本示例演示框架内置的 `TodoWriteTool`,让 LLM Agent 把多步骤任务外显成一份**结构化、可持久化的待办清单**:先规划、再逐项执行、每完成一步翻转状态。清单存放在**会话级 state**(key 前缀 `todos`**不用** `temp:`),因此可以**跨轮(跨 `Runner.run_async` 调用)存活**,Agent 能从上一轮停下的地方继续。
4+
5+
## 关键特性
6+
7+
- **整表替换语义**:模型每次调用 `todo_write` 都发送**完整的新列表**,整体替换旧列表,不做智能 merge(与 Claude Code / DeepAgents 路线一致,简单鲁棒)。
8+
- **会话级持久化**:清单写入 `tool_context.state["todos[:<branch>]"]`,随 function-response 事件的 state delta 自动落库,**跨轮存活**,无需额外存储机制。注意:框架会剥离 `temp:` 前缀的 state,因此 TodoWrite 默认使用无前缀的 `todos`
9+
- **子 Agent 隔离**:state key 追加 `:<branch>`,不同 branch(父 / 子 Agent)各自维护独立清单,互不覆盖;branch 为空时回退到 agent 名。
10+
- **硬契约校验(代码强制)**`content` / `activeForm` 非空、**至多一个 `in_progress`**`content` 全表唯一,违反则工具调用返回 `INVALID_TODOS` 错误。
11+
- **防误删守卫**:缺失 `todos` 字段或 `todos: null` 一律报错;唯一合法的清空手势是显式空数组 `todos: []`,避免上游丢字段误清整张计划。
12+
- **结构化回显**:返回 `{message, todos, oldTodos}` —— `message` 是给模型的 nudge,`todos/oldTodos` 供前端 / CLI 直接渲染当前列表与 diff,无需再查 session。
13+
- **clear_on_all_done**:全部 `completed` 时默认清空列表,避免已完成项跨轮无限堆积(本 demo 显式设为 `False` 以便展示最终全完成态)。
14+
- **NudgeHook 策略回调**:在持久化后、返回前调用的只读回调,返回的非空字符串追加进 message,可用于「预算告警 / 验证提醒 / 死循环检测」等策略而不改工具本体。
15+
- **Prompt 引导分层**:风格建议(恰好一个 in_progress、完成立即标记、不要复述整张清单)放在 `DEFAULT_TODO_PROMPT`,与硬契约清晰分层。
16+
17+
## Agent 层级结构说明
18+
19+
本例只有一个 `LlmAgent`,挂载单个 `TodoWriteTool``root_agent` 指向 `todo_planner`
20+
21+
```text
22+
todo_planner (LlmAgent)
23+
├── model: OpenAIModel
24+
├── instruction: 规划型人设(DEFAULT_TODO_PROMPT 由工具 process_request 自动注入)
25+
├── tools:
26+
│ └── TodoWriteTool(clear_on_all_done=False, nudge_hooks=[_all_done_nudge_hook])
27+
└── session: InMemorySessionService(单一 session 跨轮复用)
28+
```
29+
30+
关键文件:
31+
32+
- [examples/todo_tool/agent/agent.py](./agent/agent.py):构建 `todo_planner`,挂载 `TodoWriteTool` 并演示自定义只读 `NudgeHook`
33+
- [examples/todo_tool/agent/prompts.py](./agent/prompts.py):规划型人设 instruction(`DEFAULT_TODO_PROMPT``TodoWriteTool.process_request` 自动追加)
34+
- [examples/todo_tool/agent/config.py](./agent/config.py):环境变量读取(LLM 凭据)
35+
- [examples/todo_tool/run_agent.py](./run_agent.py):测试入口,在**同一个 session** 内驱动「规划 → 逐项完成」多轮对话,并在每轮后用 `get_todos` 读回持久化清单渲染成 ASCII checklist
36+
37+
## 关键代码解释
38+
39+
### 1) 挂载与配置(`agent/agent.py`
40+
41+
- `TodoWriteTool()` 即可直接用,工具名默认 `todo_write`(snake_case,满足部分 provider 的 `^[a-zA-Z0-9_-]+$` 命名约束)。
42+
- 构造参数:
43+
- `clear_on_all_done`(默认 `True`):全部完成时清空列表;本 demo 设为 `False` 以便看到最终「全部 `[x]`」的清单。
44+
- `state_key_prefix`(默认 `todos`):状态 key 前缀;勿用 `temp:`,该前缀不会被 SessionService 持久化。
45+
- `default_nudge`:每次成功响应追加的基础提醒。
46+
- `nudge_hooks`:只读策略回调列表。
47+
48+
### 2) 只读 NudgeHook(`_all_done_nudge_hook`
49+
50+
```python
51+
def _all_done_nudge_hook(old, new):
52+
if len(new) < 3:
53+
return None
54+
if not all(item.status == TodoStatus.COMPLETED for item in new):
55+
return None
56+
return "Reminder: all tasks are marked completed. ..."
57+
```
58+
59+
- 签名为 `(old: list[TodoItem], new: list[TodoItem]) -> Optional[str]`,在持久化后、返回前被调用。
60+
- 返回的非空字符串会追加进工具响应的 `message`,让模型看到。
61+
- 约定 **只读**:不可修改清单;本例与 Go `examples/todo` 对齐——当清单 ≥3 项且全部 `completed` 时,提醒模型在收尾前简要总结结果。
62+
63+
### 3) 跨轮持久化与读回(`run_agent.py`
64+
65+
- 所有轮次共用同一个 `session_id`,每轮一次 `runner.run_async`
66+
- 工具把清单写进 `todos:<branch>`,随事件 state delta 落库。
67+
- 每轮结束后用 `get_todos(session, branch=agent.name)`**持久化后的清单**读回来,证明它跨轮存活,再用 `render_todos` 渲染:
68+
- `[x]` 已完成(completed)
69+
- `[>]` 进行中(in_progress,显示 `activeForm`
70+
- `[ ]` 待办(pending)
71+
72+
### 4) 硬契约 vs Prompt 引导
73+
74+
- **硬契约(代码强制)**`validate_todos` —— 字段非空、至多一个 `in_progress``content` 唯一;违反返回 `INVALID_TODOS`
75+
- **防误删守卫**:缺字段 / `null` 报错,仅 `todos: []` 可清空。
76+
- **Prompt 引导(鼓励不强制)**`DEFAULT_TODO_PROMPT` 在挂载工具时经 `process_request` 自动追加到 system instruction。
77+
- 原则:要强制就加 validator,不要把约束塞进 prompt,两层保持可区分。
78+
79+
## 环境与运行
80+
81+
### 环境要求
82+
83+
- Python 3.12
84+
85+
### 安装步骤
86+
87+
```bash
88+
git clone https://github.com/trpc-group/trpc-agent-python.git
89+
cd trpc-agent-python
90+
python3 -m venv .venv
91+
source .venv/bin/activate
92+
pip3 install -e .
93+
```
94+
95+
### 环境变量要求
96+
97+
[examples/todo_tool/.env](./.env) 中配置(或通过 `export`):
98+
99+
- `TRPC_AGENT_API_KEY`
100+
- `TRPC_AGENT_BASE_URL`
101+
- `TRPC_AGENT_MODEL_NAME`
102+
103+
### 运行命令
104+
105+
```bash
106+
cd examples/todo_tool
107+
python3 run_agent.py
108+
```
109+
110+
## 运行结果(示意)
111+
112+
```text
113+
🆔 Session ID: 1a2b3c4d... (shared across all turns)
114+
115+
========== 规划任务 ==========
116+
📝 User: 请规划一个三步任务并用 todo_write 记录:1) 初始化项目骨架 2) 实现核心业务逻辑 3) 编写并跑通单元测试。先整体规划,把第一步设为进行中,其余为待办。
117+
🤖 Assistant:
118+
🔧 [Invoke Tool: todo_write({'todos': [{'content': '初始化项目骨架', 'activeForm': '正在初始化项目骨架', 'status': 'in_progress'}, {'content': '实现核心业务逻辑', 'activeForm': '正在实现核心业务逻辑', 'status': 'pending'}, {'content': '编写并跑通单元测试', 'activeForm': '正在编写单元测试', 'status': 'pending'}]})]
119+
📊 [Tool Result: items=3 old_items=0 [in_progress:初始化项目骨架, pending:实现核心业务逻辑, pending:编写并跑通单元测试]]
120+
我已经把任务拆成三步,现在开始第一步:初始化项目骨架。
121+
122+
📋 Persisted checklist:
123+
[>] 正在初始化项目骨架
124+
[ ] 实现核心业务逻辑
125+
[ ] 编写并跑通单元测试
126+
----------------------------------------
127+
128+
========== 完成第 1 步 ==========
129+
📝 User: 第一步『初始化项目骨架』已经完成了,请更新清单并开始下一步。
130+
🤖 Assistant:
131+
🔧 [Invoke Tool: todo_write({'todos': [{'content': '初始化项目骨架', 'activeForm': '正在初始化项目骨架', 'status': 'completed'}, {'content': '实现核心业务逻辑', 'activeForm': '正在实现核心业务逻辑', 'status': 'in_progress'}, {'content': '编写并跑通单元测试', 'activeForm': '正在编写单元测试', 'status': 'pending'}]})]
132+
📊 [Tool Result: items=3 old_items=3 [completed:初始化项目骨架, in_progress:实现核心业务逻辑, pending:编写并跑通单元测试]]
133+
第一步已完成,现在进行第二步:实现核心业务逻辑。
134+
135+
📋 Persisted checklist:
136+
[x] 初始化项目骨架
137+
[>] 正在实现核心业务逻辑
138+
[ ] 编写并跑通单元测试
139+
----------------------------------------
140+
141+
... (第 2、3 步依次翻转,最终全部 [x] 完成)
142+
```
143+
144+
## 适用场景建议
145+
146+
- 复杂多步任务(代码生成、多文件改造、调研、部署)需要**规划外显 + 进度可视 + 可控收尾**:直接复用本示例。
147+
- 需要把清单接入前端 / AG-UI 实时渲染:消费工具响应里的 `todos` / `oldTodos`(已是纯 JSON 结构)。
148+
- 需要服务端 / REST / 审计读取当前清单:调用 `get_todos(session, branch)`
149+
- 需要在工具调用前后插入日志、审计、参数校验:把 `TodoWriteTool(filters_name=[...])``before_tool_callback` / `after_tool_callback` 组合使用。
150+
- 需要「未完成项不允许收尾」的强约束:用 `LlmAgent``after_model_callback` / `before_model_callback` 实现 enforcer(参考实现方案文档的 Phase 2)。
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
2+
#
3+
# Copyright (C) 2026 Tencent. All rights reserved.
4+
#
5+
# tRPC-Agent-Python is licensed under Apache-2.0.

examples/todo_tool/agent/agent.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
2+
#
3+
# Copyright (C) 2026 Tencent. All rights reserved.
4+
#
5+
# tRPC-Agent-Python is licensed under Apache-2.0.
6+
""" Agent module for the TodoWriteTool example"""
7+
8+
from typing import List
9+
from typing import Optional
10+
11+
from trpc_agent_sdk.agents import LlmAgent
12+
from trpc_agent_sdk.models import LLMModel
13+
from trpc_agent_sdk.models import OpenAIModel
14+
from trpc_agent_sdk.tools import TodoItem
15+
from trpc_agent_sdk.tools import TodoStatus
16+
from trpc_agent_sdk.tools import TodoWriteTool
17+
18+
from .config import get_model_config
19+
from .prompts import INSTRUCTION
20+
21+
22+
def _create_model() -> LLMModel:
23+
"""Create the LLM model used by the demo agent."""
24+
api_key, url, model_name = get_model_config()
25+
return OpenAIModel(model_name=model_name, api_key=api_key, base_url=url)
26+
27+
28+
def _all_done_nudge_hook(old: List[TodoItem], new: List[TodoItem]) -> Optional[str]:
29+
"""Example read-only NudgeHook (aligned with Go ``examples/todo``).
30+
31+
When the plan has at least three items and every item is ``completed``,
32+
append a reminder so the model summarises the outcome before wrapping up.
33+
"""
34+
if len(new) < 3:
35+
return None
36+
if not all(item.status == TodoStatus.COMPLETED for item in new):
37+
return None
38+
return ("Reminder: all tasks are marked completed. "
39+
"Before finishing, briefly summarise the outcome for the user.")
40+
41+
42+
def create_todo_agent() -> LlmAgent:
43+
"""Build an agent that plans and tracks work with ``TodoWriteTool``.
44+
45+
``clear_on_all_done=False`` keeps completed items visible so the demo
46+
can render the final all-done checklist; production agents may keep
47+
the default (``True``) to avoid stale items piling up across turns.
48+
"""
49+
todo_tool = TodoWriteTool(
50+
clear_on_all_done=False,
51+
nudge_hooks=[_all_done_nudge_hook],
52+
)
53+
return LlmAgent(
54+
name="todo_planner",
55+
description="Engineering assistant that plans and tracks multi-step tasks with a todo checklist.",
56+
model=_create_model(),
57+
instruction=INSTRUCTION,
58+
tools=[todo_tool],
59+
)
60+
61+
62+
todo_agent = create_todo_agent()
63+
root_agent = todo_agent

examples/todo_tool/agent/config.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
2+
#
3+
# Copyright (C) 2026 Tencent. All rights reserved.
4+
#
5+
# tRPC-Agent-Python is licensed under Apache-2.0.
6+
""" Agent config module"""
7+
8+
import os
9+
10+
11+
def get_model_config() -> tuple[str, str, str]:
12+
"""Get LLM model config from environment variables."""
13+
api_key = os.getenv('TRPC_AGENT_API_KEY', '')
14+
url = os.getenv('TRPC_AGENT_BASE_URL', '')
15+
model_name = os.getenv('TRPC_AGENT_MODEL_NAME', '')
16+
if not api_key or not url or not model_name:
17+
raise ValueError('''TRPC_AGENT_API_KEY, TRPC_AGENT_BASE_URL,
18+
and TRPC_AGENT_MODEL_NAME must be set in environment variables''')
19+
return api_key, url, model_name
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
2+
#
3+
# Copyright (C) 2026 Tencent. All rights reserved.
4+
#
5+
# tRPC-Agent-Python is licensed under Apache-2.0.
6+
""" prompts for the TodoWrite demo agent"""
7+
8+
# Demo-specific persona only. ``DEFAULT_TODO_PROMPT`` is injected automatically
9+
# by ``TodoWriteTool.process_request`` when the tool is registered on the agent.
10+
INSTRUCTION = ("You are a rigorous engineering assistant that breaks a task into a checklist and works "
11+
"through it step by step.\n"
12+
"\n"
13+
"Behaviour for this demo:\n"
14+
" 1. For any task with more than two steps, FIRST call `todo_write` to lay out the full plan, "
15+
"with the first item set to `in_progress` and the rest `pending`.\n"
16+
" 2. On each follow-up turn, advance the plan: mark the finished item `completed` and set the "
17+
"next item to `in_progress` in the SAME `todo_write` call.\n"
18+
" 3. Always send the COMPLETE list — it replaces the previous one.\n"
19+
" 4. After updating the list, briefly tell the user what you just did and what is next; do not "
20+
"paste the whole checklist back.")

0 commit comments

Comments
 (0)