@@ -35,6 +35,7 @@ Agent 通过以下步骤动态使用工具:
3535| [ WebSearchTool] ( #websearchtool ) | 公网搜索引擎检索 | 实例化 WebSearchTool 并加入 tools | 实时资讯、版本发布、事实/定义查询 |
3636| [ TodoWriteTool] ( #todowritetool-任务清单工具 ) | 多步任务规划与进度跟踪(整表替换) | 挂载 ` TodoWriteTool ` | 短清单、无依赖编排、token 不敏感 |
3737| [ Task 工具族] ( #task-工具族结构化任务看板 ) | 结构化任务看板(按 id 增量更新 + 依赖) | 挂载 ` TaskToolSet ` | 长任务板、跨轮跟踪、blockedBy 依赖 |
38+ | [ Goal 工具族] ( #goal-工具族持久会话目标 ) | 单会话持久目标 + 强制收尾 | ` setup_goal(agent) ` | 跨轮大目标、宿主设目标、未完成不许收尾 |
3839| [ Agent Code Executor] ( ./code_executor.md ) | 自动生成并执行代码场景、数据处理场景 | 配置 CodeExecutor | API 自动调用、表格数据处理 |
3940---
4041
@@ -3040,3 +3041,163 @@ print(render_task_list(store))
30403041| --- | --- |
30413042| [ examples/task_tools] ( ../../../examples/task_tools/ ) | 多轮对话:依赖编排、逐项完成、跨轮 ` get_task_store ` 读回看板 |
30423043| [ examples/task_tools_parallel] ( ../../../examples/task_tools_parallel/ ) | 验证 ` parallel_tool_calls ` 与 ` task_store_lock ` (Phase 1–2 无需 API Key) |
3044+
3045+ ---
3046+
3047+ ## Goal 工具族(持久会话目标)
3048+
3049+ ` GoalToolSet ` 暴露三个工具——` create_goal ` 、` get_goal ` 、` update_goal ` ——对齐 Claude Code 的 ** Session Goal** 能力。与 ` TodoWriteTool ` (多行待办)和 ` TaskToolSet ` (多任务看板)不同,Goal 在每个 session branch 上** 至多只有一个** 持久目标:在目标为 ` active ` 期间,模型给出「看起来像最终答案」的文本** 不算完成** ——必须继续执行,或显式调用 ` update_goal('complete' | 'blocked') ` 收尾。
3050+
3051+ 目标序列化为** 单个 JSON blob** (` GoalRecord ` )写入 ` tool_context.state["goal[:<branch>]"] ` ,跨 ` Runner.run_async ` 调用存活。除三个模型工具外,完整能力还需通过 ` setup_goal() ` 挂载一对 ** enforcement callbacks** (` before_model ` / ` after_model ` ),在** 同一次 invocation 内** 拦截过早的最终回复并自动重试。
3052+
3053+ ### 功能特性
3054+
3055+ - ** 单目标契约** :每个 branch 一个 ` GoalRecord ` (` objective ` + 三态 ` active ` / ` complete ` / ` blocked ` );` complete ` / ` blocked ` 为** 不可逆** 终态
3056+ - ** 跨轮持久化** :随 function-response 的 state delta 落库;** 勿用** ` temp: ` 前缀
3057+ - ** 子 Agent 隔离** :state key 追加 ` :<branch> ` ,父 / 子 Agent 各自维护独立目标
3058+ - ** 强制收尾(enforcement)** :目标 ` active ` 时,` after_model ` 检测「无 tool call、有可见文本、非 partial」的过早 final,抑制该回复并在同 invocation 内 re-run;` before_model ` 注入 user-role nudge
3059+ - ** fail-open 预算** :` max_retries ` (默认 3)次拦截后放行最终回复,避免无限循环;计数器存在 invocation 级 ` agent_context.metadata ` ,不持久化
3060+ - ** 双入口创建** :
3061+ - ** 模型侧** :` create_goal(objective=...) ` —— LLM 判断多步任务后自主创建
3062+ - ** 宿主侧** :` start_goal(session_service, ...) ` —— 应用层在首轮前写入 session,模型无需调用 ` create_goal `
3063+ - ** Prompt 引导分层** :` DEFAULT_GUIDANCE ` 在目标 active 时经 ` before_model ` 注入 system instruction(` inject_guidance=True ` );硬约束由 store 校验 + callback 共同保证
3064+ - ** 并发安全** :` _GoalToolBase ` 在 load → mutate → save 外包 ` goal_store_lock ` (按 session + branch),兼容 ` parallel_tool_calls=True `
3065+
3066+ ### 与 Todo / Task 的关系
3067+
3068+ | 维度 | ` TodoWriteTool ` | ` TaskToolSet ` | Goal 工具族 |
3069+ | --- | --- | --- | --- |
3070+ | 粒度 | 多行待办清单 | 多任务看板 + 依赖 | ** 单个** 会话目标 |
3071+ | 更新方式 | 整表替换 | 按 ` taskId ` 增量 | ` create_goal ` / ` update_goal ` |
3072+ | 未完成能否收尾 | Prompt 引导 | Prompt 引导 | ** callback 强制拦截** |
3073+ | state key | ` todos[:branch] ` | ` tasks[:branch] ` | ` goal[:branch] ` |
3074+ | 典型用途 | 步骤可视、短清单 | 长看板、依赖编排 | 整件事是否算做完 |
3075+
3076+ > Todo / Task 管「步骤分解」,Goal 管「整体完成契约」。可组合使用,但避免让模型同时混用过多规划工具。
3077+
3078+ ### GoalOptions 构造参数
3079+
3080+ 通过 ` setup_goal(agent, GoalOptions(...)) ` 配置:
3081+
3082+ | 参数 | 类型 | 默认值 | 说明 |
3083+ | ------| ------| --------| ------|
3084+ | ` state_key_prefix ` | ` str ` | ` "goal" ` | state key 前缀;勿使用 ` temp: ` |
3085+ | ` inject_guidance ` | ` bool ` | ` True ` | 是否在 ` before_model ` 向 system instruction 注入 ` DEFAULT_GUIDANCE ` |
3086+ | ` guidance ` | ` str ` | ` DEFAULT_GUIDANCE ` | 注入的长文案(含串行调用 goal 工具等约定) |
3087+ | ` max_retries ` | ` int ` | ` 3 ` | 同 invocation 内拦截过早 final 的预算;耗尽后 fail-open |
3088+ | ` nudge_template ` | ` str ` | ` DEFAULT_NUDGE ` | 拦截后以 user-role 追加的提醒模板(支持 ` {attempt} ` / ` {max_retries} ` / ` {objective} ` ) |
3089+ | ` on_retry ` | ` Callable[[RetryEvent], None] ` | ` None ` | 每次拦截或预算耗尽时的可观测回调 |
3090+
3091+ 仅挂载模型工具、不要 enforcement 时,可直接 ` tools=[GoalToolSet()] ` ,但不具备「未完成不许收尾」能力。
3092+
3093+ ### 三个工具的 LLM 参数概要
3094+
3095+ ** ` create_goal ` **
3096+
3097+ | 参数 | 必填 | 说明 |
3098+ | ------| ------| ------|
3099+ | ` objective ` | 是 | 完成标准——「done」具体指什么 |
3100+
3101+ 成功返回 ` {message, goal} ` ;若已有 ` active ` 目标返回 ` {error: "INVALID_STATE: ..."} ` 。
3102+
3103+ ** ` get_goal ` **
3104+
3105+ 无参数。有目标时返回 ` {message, goal} ` ;无目标时返回 ` {message: "No session goal is set."} ` 。
3106+
3107+ ** ` update_goal ` **
3108+
3109+ | 参数 | 必填 | 说明 |
3110+ | ------| ------| ------|
3111+ | ` status ` | 是 | ` complete ` (目标已达成)或 ` blocked ` (同一阻塞条件反复出现、无用户输入无法继续) |
3112+
3113+ 成功返回 ` {message, goal} ` ;无 active 目标或已是终态时返回 ` {error: "INVALID_STATE: ..."} ` 。
3114+
3115+ ** ` GoalRecord ` 字段** (JSON 使用 camelCase 别名持久化):
3116+
3117+ | 字段 | 说明 |
3118+ | ------| ------|
3119+ | ` id ` | 服务端分配的 uuid |
3120+ | ` objective ` | 完成标准文本 |
3121+ | ` status ` | ` active ` / ` complete ` / ` blocked ` |
3122+ | ` createdAtUnix ` / ` updatedAtUnix ` | 创建 / 最后更新时间(unix 秒) |
3123+ | ` terminalAtUnix ` | 进入终态的时间(可选) |
3124+
3125+ ### enforcement 工作流程
3126+
3127+ ``` text
3128+ 模型输出 final 文本(无 tool call,goal 仍 active)
3129+ ↓
3130+ after_model 判定为 premature final
3131+ ↓
3132+ 抑制该 final(不提交为答案),retry_count += 1
3133+ before_model 注入 nudge,同 invocation 继续 agent loop
3134+ ↓
3135+ retry_count >= max_retries → fail-open,on_retry(reason="exhausted")
3136+ ```
3137+
3138+ 拦截条件(` _is_premature_final ` ):非 partial、无 error、content 含可见文本,且** 不含** ` function_call ` / ` function_response ` 。
3139+
3140+ ### 使用方式
3141+
3142+ 推荐一行挂载工具 + callbacks:
3143+
3144+ ``` python
3145+ from trpc_agent_sdk.agents import LlmAgent
3146+ from trpc_agent_sdk.models import OpenAIModel
3147+ from trpc_agent_sdk.tools.goal_tools import GoalOptions, RetryEvent, setup_goal
3148+
3149+ def on_retry (event : RetryEvent) -> None :
3150+ if event.reason == " blocked" :
3151+ print (f " 拦截过早收尾 (attempt { event.attempt_number} / { event.max_retries} ) " )
3152+
3153+ agent = LlmAgent(
3154+ name = " goal_agent" ,
3155+ model = OpenAIModel(model_name = " ..." , api_key = " ..." , base_url = " ..." ),
3156+ instruction = " 多步工程任务请用 goal 工具跟踪完成状态。" ,
3157+ tools = [... ], # 业务工具,如 BashTool / WriteTool
3158+ )
3159+ setup_goal(agent, GoalOptions(max_retries = 3 , on_retry = on_retry))
3160+ ```
3161+
3162+ 宿主在首轮前预注入目标(模型不调用 ` create_goal ` ):
3163+
3164+ ``` python
3165+ from trpc_agent_sdk.tools.goal_tools import start_goal
3166+
3167+ goal = await start_goal(
3168+ session_service,
3169+ app_name = " my_app" ,
3170+ user_id = " user_1" ,
3171+ session_id = session_id,
3172+ objective = " 在当前目录创建 notes/ 并写入 summary.txt 与 example.py" ,
3173+ agent_name = agent.name, # 与 LlmAgent.name 一致,用于 branch 隔离
3174+ )
3175+ ```
3176+
3177+ 读回持久化目标(REST / 审计 / demo 收尾):
3178+
3179+ ``` python
3180+ from trpc_agent_sdk.tools.goal_tools import get_goal_record, render_goal
3181+
3182+ goal = get_goal_record(session, branch = agent.name)
3183+ print (render_goal(goal))
3184+ # ✅ Goal [complete]
3185+ # objective: ...
3186+ # created: 1782893110
3187+ # terminal: 1782893116
3188+ ```
3189+
3190+ ### Goal 工具族最佳实践
3191+
3192+ - ** 用 ` setup_goal ` 而非只挂 ` GoalToolSet ` ** :只有 callbacks 才能实现「active 期间不许 final」
3193+ - ** 模型侧 vs 宿主侧** :Slash command、` /goal ` 、配置驱动任务用 ` start_goal() ` ;让模型自主判断多步任务时用 ` create_goal `
3194+ - ** 一次响应只调一个 goal 工具** :` DEFAULT_GUIDANCE ` 要求串行语义;不要同轮 ` create_goal ` + ` update_goal `
3195+ - ** ` blocked ` 慎用** :仅当同一阻塞条件跨多次尝试仍无法推进、且需要用户输入或外部状态变化时使用;不要因为任务难、慢或不完整就标记 blocked
3196+ - ** 可观测性** :通过 ` on_retry ` 记录 ` ⚡ Premature final intercepted ` 与预算耗尽,便于调优 prompt 或 ` max_retries `
3197+ - ** 与 Todo / Task 分工** :Todo / Task 展示步骤与依赖;Goal 约束「整件事是否已真正完成」
3198+
3199+ ### Goal 工具族完整示例
3200+
3201+ | 示例 | 说明 |
3202+ | --- | --- |
3203+ | [ examples/goal_tools] ( ../../../examples/goal_tools/ ) | Case 1:模型 ` create_goal ` ;Case 2:宿主 ` start_goal ` 预注入;演示 enforcement 拦截与 ` update_goal(complete) ` |
0 commit comments