Skip to content

Commit 2768dc2

Browse files
Feat: tRPC-Agent对齐Claude Code Plan Mod能力
1 parent 4336564 commit 2768dc2

45 files changed

Lines changed: 6699 additions & 717 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/mkdocs/en/goal.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
## Goal Tool Family (Persistent Session Goal)
2+
3+
`GoalToolSet` exposes three tools — `create_goal`, `get_goal`, `update_goal` — aligned with Claude Code **Session Goal** capabilities. Unlike `TodoWriteTool` (multi-item checklist) and `TaskToolSet` (multi-task board), Goal maintains **at most one** persistent objective per session branch: while status is `active`, a response that **looks like a final answer** does **not** mean the work is done — the model must keep working or explicitly call `update_goal('complete' | 'blocked')`.
4+
5+
The goal is serialized as a **single JSON blob** (`GoalRecord`) in `tool_context.state["goal[:<branch>]"]`, surviving across `Runner.run_async` calls. Beyond the three model tools, the full capability requires `setup_goal()` to mount **enforcement callbacks** (`before_model` / `after_model`) that intercept premature final responses and re-run within the **same invocation**.
6+
7+
### Features
8+
9+
- **Single-goal contract**: one `GoalRecord` per branch (`objective` + three states `active` / `complete` / `blocked`); `complete` / `blocked` are **irreversible** terminal states
10+
- **Cross-turn persistence**: persisted via function-response state deltas; **do not** use the `temp:` prefix
11+
- **Sub-agent isolation**: state key appends `:<branch>`
12+
- **Enforced completion**: while `active`, `after_model` detects premature finals (no tool call, visible text, non-partial), suppresses them, and re-runs in the same invocation; `before_model` injects a user-role nudge
13+
- **Fail-open budget**: after `max_retries` (default 3) interceptions, the final response is allowed so the loop cannot spin forever; counters live in invocation-scoped `agent_context.metadata` and are not persisted
14+
- **Two creation paths**:
15+
- **Model side**: `create_goal(objective=...)` — LLM creates after judging a multi-step task
16+
- **Host side**: `start_goal(session_service, ...)` — application writes the goal before the first turn; the model does not call `create_goal`
17+
- **Layered prompt guidance**: `DEFAULT_GUIDANCE` injected into system instruction via `before_model` when `inject_guidance=True`; hard rules enforced by store validation + callbacks
18+
- **Concurrency safety**: `_GoalToolBase` wraps load → mutate → save in `goal_store_lock` (per session + branch), compatible with `parallel_tool_calls=True`
19+
20+
### Relationship to Todo / Task
21+
22+
| Dimension | `TodoWriteTool` | `TaskToolSet` | Goal Tool Family |
23+
| --- | --- | --- | --- |
24+
| Granularity | Multi-item checklist | Multi-task board + deps | **Single** session objective |
25+
| Update style | Full-list replace | Incremental by `taskId` | `create_goal` / `update_goal` |
26+
| Can finish while incomplete? | Prompt guidance | Prompt guidance | **Callback enforcement** |
27+
| State key | `todos[:branch]` | `tasks[:branch]` | `goal[:branch]` |
28+
| Typical use | Step visibility, short lists | Long boards, dependencies | Whether the whole job is truly done |
29+
30+
> Todo / Task handle **step decomposition**; Goal handles the **overall completion contract**. They can be combined, but avoid mounting too many planning tools at once.
31+
32+
### GoalOptions Constructor Parameters
33+
34+
Configure via `setup_goal(agent, GoalOptions(...))`:
35+
36+
| Parameter | Type | Default | Description |
37+
|------|------|--------|------|
38+
| `state_key_prefix` | `str` | `"goal"` | State key prefix; do not use `temp:` |
39+
| `inject_guidance` | `bool` | `True` | Inject `DEFAULT_GUIDANCE` into system instruction in `before_model` |
40+
| `guidance` | `str` | `DEFAULT_GUIDANCE` | Long guidance text (serial goal-tool calls, etc.) |
41+
| `max_retries` | `int` | `3` | Same-invocation budget for intercepting premature finals; fail-open when exhausted |
42+
| `nudge_template` | `str` | `DEFAULT_NUDGE` | User-role reminder after interception; supports `{attempt}` / `{max_retries}` / `{objective}` |
43+
| `on_retry` | `Callable[[RetryEvent], None]` | `None` | Observability callback on each interception or budget exhaustion |
44+
45+
Mounting only `GoalToolSet()` without enforcement gives model-facing tools but **not** "no final while active".
46+
47+
### LLM Parameters for the Three Tools
48+
49+
**`create_goal`**
50+
51+
| Parameter | Required | Description |
52+
|------|------|------|
53+
| `objective` | Yes | Completion criteria — what "done" concretely means |
54+
55+
Success: `{message, goal}`; if an `active` goal already exists: `{error: "INVALID_STATE: ..."}`.
56+
57+
**`get_goal`**
58+
59+
No parameters. With a goal: `{message, goal}`; without: `{message: "No session goal is set."}`.
60+
61+
**`update_goal`**
62+
63+
| Parameter | Required | Description |
64+
|------|------|------|
65+
| `status` | Yes | `complete` (objective met) or `blocked` (same blocker repeats; cannot proceed without user input) |
66+
67+
Success: `{message, goal}`; no active goal or already terminal: `{error: "INVALID_STATE: ..."}`.
68+
69+
**`GoalRecord` fields** (persisted with camelCase JSON aliases):
70+
71+
| Field | Description |
72+
|------|------|
73+
| `id` | Server-assigned uuid |
74+
| `objective` | Completion criteria text |
75+
| `status` | `active` / `complete` / `blocked` |
76+
| `createdAtUnix` / `updatedAtUnix` | Created / last-updated time (unix seconds) |
77+
| `terminalAtUnix` | Time entered a terminal state (optional) |
78+
79+
### Enforcement Workflow
80+
81+
```text
82+
Model outputs final text (no tool call, goal still active)
83+
84+
after_model classifies as premature final
85+
86+
Suppress final (not committed as answer), retry_count += 1
87+
before_model injects nudge, same invocation continues agent loop
88+
89+
retry_count >= max_retries → fail-open, on_retry(reason="exhausted")
90+
```
91+
92+
Interception condition (`_is_premature_final`): non-partial, no error, visible text in content, and **no** `function_call` / `function_response`.
93+
94+
### Usage
95+
96+
Recommended one-line mount of tools + callbacks:
97+
98+
```python
99+
from trpc_agent_sdk.agents import LlmAgent
100+
from trpc_agent_sdk.models import OpenAIModel
101+
from trpc_agent_sdk.tools.goal_tools import GoalOptions, RetryEvent, setup_goal
102+
103+
def on_retry(event: RetryEvent) -> None:
104+
if event.reason == "blocked":
105+
print(f"Premature final intercepted (attempt {event.attempt_number}/{event.max_retries})")
106+
107+
agent = LlmAgent(
108+
name="goal_agent",
109+
model=OpenAIModel(model_name="...", api_key="...", base_url="..."),
110+
instruction="Use goal tools to track completion for multi-step engineering tasks.",
111+
tools=[...], # Business tools, e.g. BashTool / WriteTool
112+
)
113+
setup_goal(agent, GoalOptions(max_retries=3, on_retry=on_retry))
114+
```
115+
116+
Host pre-injects a goal before the first turn (model does not call `create_goal`):
117+
118+
```python
119+
from trpc_agent_sdk.tools.goal_tools import start_goal
120+
121+
goal = await start_goal(
122+
session_service,
123+
app_name="my_app",
124+
user_id="user_1",
125+
session_id=session_id,
126+
objective="Create notes/ with summary.txt and example.py in the current directory",
127+
agent_name=agent.name, # Match LlmAgent.name for branch isolation
128+
)
129+
```
130+
131+
Read back the persisted goal (REST / audit / demo wrap-up):
132+
133+
```python
134+
from trpc_agent_sdk.tools.goal_tools import get_goal_record, render_goal
135+
136+
goal = get_goal_record(session, branch=agent.name)
137+
print(render_goal(goal))
138+
# ✅ Goal [complete]
139+
# objective: ...
140+
# created: 1782893110
141+
# terminal: 1782893116
142+
```
143+
144+
### Goal Tool Family Best Practices
145+
146+
- **Use `setup_goal`, not `GoalToolSet` alone**: only callbacks enforce "no final while active"
147+
- **Model vs host**: slash commands, `/goal`, config-driven tasks → `start_goal()`; let the model judge multi-step work → `create_goal`
148+
- **One goal tool per response**: `DEFAULT_GUIDANCE` requires serial semantics; do not call `create_goal` and `update_goal` in the same turn
149+
- **Use `blocked` sparingly**: only when the same blocker repeats across attempts and user input or external state change is required; do not mark blocked because work is hard, slow, or incomplete
150+
- **Observability**: use `on_retry` to log premature-final interceptions and budget exhaustion when tuning prompt or `max_retries`
151+
- **Division of labor with Todo / Task**: Todo / Task show steps and dependencies; Goal constrains whether the whole job is truly finished
152+
153+
### Goal Tool Family Complete Example
154+
155+
| Example | Description |
156+
| --- | --- |
157+
| [examples/goal_tools](../../../examples/goal_tools/) | Case 1: model `create_goal`; Case 2: host `start_goal` pre-injection; demonstrates enforcement interception and `update_goal(complete)` |

docs/mkdocs/en/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Welcome to the English documentation for tRPC-Agent-Python.
1616
- [Memory](./memory.md): Store and retrieve long-term memories.
1717
- [Knowledge](./knowledge.md): Build RAG workflows with LangChain components.
1818
- [Multi-Agent](./multi_agents.md): Compose agents for complex workflows.
19+
- [Plan Mode](./plan.md): Design-then-implement workflow with write gate and HITL approval.
1920
- [Evaluation](./evaluation.md): Evaluate agent behavior and response quality.
2021

2122
For source code and examples, see the [GitHub repository](https://github.com/trpc-group/trpc-agent-python).

0 commit comments

Comments
 (0)