Task Prompt Architecture And Agent Evolution

Current legacy prompt architecture(2026-07-15):本文的 push/pull context、schema/writer 分层和 self-check 方法仍可参考;Objective-first、create_closed lead_follow_up、Category Matrix 与 global closeResult 不是 Target。现行 Task/AI runtime contract 见 Task System Design V3

0. 一句话

retaintive 是一台 把客户那边发生的任何事,翻译成该达成的目标、该做的事 的机器。

  • 发生的事 = call、SMS、voicemail、lead form;以后还会有 Salesforce、Slack、email、web form。
  • 翻出来的 = 今天主要是 taskDecisions[] / task;产品上正在演进成更稳定的 objective
  • 中间那层翻译 = AI 做判断,但 AI 只提出 proposal,不拥有最终写入权。

所以所有 AI/task 设计争议,本质都是同一个问题:这台翻译机该信什么、谁说了算。

本文记录两件事:

  1. 今天怎么系统性改 contacts-analyzer prompt,不再盲改。
  2. 未来怎么从 push context 走向 pull context / self-check agent,但不在底座没稳时上 agent。

1. 当前现实:一个 call,内部分层

当前 contacts-analyzer 还没有拆成两个 AI call。runtime 仍然是一个 combined prompt,一次输出:

  • contact profile fields
  • taskDecisions[]

但是 prompt 内部必须按职责分层:

system prompt + user prompt + Zod schema
  -> model output
  -> deterministic fallback
  -> writer / Policy Guard
  -> DB / audit artifacts

当前 combined system prompt 应该按这个顺序组织:

1. Purpose / worldview
2. Evidence Authority
3. Contact Profile Policy
4. Task Decision State Machine
5. Action Semantics
6. Generated Taxonomy
7. Category Matrix
8. Examples / edge cases
9. Output Contract
10. Input Context Contract

在 code 里对应:

Section责任例子
contacts-purpose说明这套 AI 是什么机器customer-side events -> objective / work
contacts-evidence-authority共享证据裁判规则fresh intent、hard facts、hard stops、writer authority
contacts-profile-policyContact 判断lifecycle、leadStatus、DNC、summary、goals、objections
contacts-task-decision-policyTask 判断state machine、action semantics、category matrix、examples
contacts-output-contractJSON shape reminder每个 action required / optional fields
contacts-input-contextuser prompt 格式说明RECENT CALLSOPEN TASKStaskRefsourceRefs

2. System prompt / user prompt / schema / writer 分工

Layer放什么不放什么
system prompt稳定判断标准、Evidence Authority、Contact/Task policy、taxonomy 用法、examples某个 contact 的 runtime facts
user prompt单个 contact 的事实材料:stored snapshot、calls、messages、leads、open tasks、closed tasks、playbook guidance稳定 business policy
Zod schemaJSON shape、enum、action shape 互斥,以及哪些 model output shape 可以进入 deterministic fallback业务推理理由、最终业务写入权
writer / Policy Guard最终写入权威:DNC、store isolation、duplicate、taskRef resolve、evidence attach、DB constraints让模型自由写库
evalexpected behavior、pass criteria、known gaps、case explanation生产写库逻辑

关键规则:

  • prompt 说 MUST 不够;如果字段是最终写入必需,schema、fallback、writer 必须合起来保证它不会以坏状态落库。
  • schema 不是永远越严越好。对模型常见但可确定修复的半成品输出,例如 fieldless update,schema 可以先放行,让 deterministic fallback 有机会修成 record_progress / close / valid update
  • schema 通过不代表业务正确;writer / Policy Guard 仍然要二次校验。
  • user prompt 只是事实输入,不应该塞长期 policy。
  • stale contact snapshot 是 context,不是 fresh task intent 的硬 gate。

3. Contact 和 Task 的边界

Contact 回答:

这个人是谁?
他们现在是 lead / member / churned / unknown?
关系状态和 profile 怎么更新?

Task 回答:

客户侧发生的事说明哪个 objective 状态变了?
应该 create_open、create_closed、close、update、record_progress,还是 []?

Shared / Evidence Authority 回答:

多条证据冲突时信谁?
谁拥有最终写入权?

重要边界:

A terminal lifecycle state stops proactive future profile-driven outreach.
It does not erase material task ledger outcomes such as create_closed.

例子:

  • booked = terminal 是 lead first-booking lifecycle 的 contact 规则。
  • 但如果最新 interaction 里 new lead 当场 booked,且没有 open task,task 层仍应 create_closed lead_follow_up/booked 记 ledger。
  • 如果已经有 open lead_follow_up,则 close T1 booked,不是 create_closed

4. Prompt 里必须有什么

每次改 contacts-analyzer prompt,都检查这些 section 是否存在且职责清楚:

必须 section为什么必须有
Purpose / worldview让模型知道它不是分类器,而是 event -> objective / work translator
Evidence Authority解决 stale snapshot vs fresh interaction、hard facts、hard stops
Contact Profile Policylifecycle/profile 规则有自己的边界,不误伤 task
Task Decision State Machine让模型先判断 action path,不靠零散 examples 猜
Action Semantics每个 action 的业务含义和字段含义
Generated Taxonomyenum / definitions 从 source of truth 注入
Category Matrix怎么把业务 evidence 映射到 typeCategory / closeResult / progressType
Examples / edge cases只放高信号边界,覆盖 eval 失败模式
Output Contract让模型知道 JSON shape;和 schema 保持一致
Input Context Contract让模型知道 user prompt 的 facts 怎么读

5. Taxonomy 使用原则

taxonomy 的 source of truth 是 code/common,不是 prompt 手写文本。

callytics-infrastructure 里:

  • Task taxonomy 从 @retaintive/common/db@retaintive/common/taxonomy/task 动态注入。
  • Contact taxonomy 从 @retaintive/common/taxonomy/contact 动态注入。
  • Prompt 只应该写“怎么用 taxonomy 做判断”,不要复制第二份 enum/definition。

如果发现 taxonomy definition 本身太薄,例如:

  • referral 不够说明 guest pass / friend details / referral credit 边界。
  • renewal 不够说明 payment recovery vs freeze expiry。
  • retention 不够说明 complaint / billing dispute / service issue。

正确动作:

优先改 callytics-common taxonomy definition
  -> prompt registry 自动注入
  -> tests / snapshot / eval 一起验证

不要只在 prompt 里 patch 一句,因为那会让别的 consumer 继续吃旧 taxonomy。

6. 怎么避免信息重复

目标不是“同一个词永远不能出现两次”,而是每层不抢职责。

合理重复:

Task policy: 什么时候用 update
Output contract: real update 应该输出什么 shape,例如 non-empty suggestedActions
Zod schema: 接受可修复的 model shape,让 deterministic fallback 有机会运行
Writer: 真正落库的 update 没有 payload 不写库 / 不信模型

这是 contract chain,不是重复。

不合理重复:

  • contact section 讲完整 task state machine。
  • task section 重写 full lifecycle profile policy。
  • user prompt 注入长期 business policy。
  • prompt 手写复制 common taxonomy。
  • examples 试图覆盖所有 enum 全排列。

7. Prompt accuracy 修改流程

Phase 1:还原现状,不改东西

先回答“AI 到底看到了什么”:

  1. 拼出当前 assembled system prompt
  2. 找出 userMessage 实际包含哪些字段。
  3. 确认 Zod schema 对输出有什么 admission rule 和硬约束。
  4. 看 deterministic fallback 和 writer / Policy Guard 怎么消费 output。
  5. 画出当前链路。

常看代码:

lambda/contacts-analyzer/src/core/prompts/index.ts
lambda/contacts-analyzer/src/core/prompts/*
lambda/contacts-analyzer/src/core/prompt-builder.ts
lambda/contacts-analyzer/src/core/models.ts
lambda/contacts-analyzer/src/handler.ts
lambda/contacts-analyzer/src/infrastructure/neon-repository.ts
lambda/contacts-analyzer/scripts/golden-eval/*

Phase 2:按层拆问题

每个问题都要归因到一层:

失败模式可能层
模型没看到 factsuser prompt / context builder
模型看到 facts 但 action 选错system prompt policy
模型 action 对但 payload 缺字段output contract / Zod schema
模型 action 半成品可修复,但 parse 前被拒Zod schema admission / deterministic fallback
模型输出对但没写库writer / Policy Guard / DB constraints
expected 本身有争议eval / product decision

Phase 3:先写结构,再写文字

不要直接 patch 一句。先说明要改哪个 section:

Shared -> Evidence Authority
Contact -> lifecycle/profile/DNC
Task -> state machine/action/category/examples
Schema -> admission rules / required fields / enum
Writer -> final authority/safety
Eval -> expected behavior

Phase 4:用 eval failures 验证结构

每个失败 case 用这个模板:

Case:
Tests:
Expected:
Actual:
Why wrong:
Likely layer:
Recommended fix:

重点看这些模式:

  • create_closed 被输出成 []
  • referral 边界弱。
  • record_progress vs update 混。
  • update 选对了但缺 suggestedActions
  • stale contact snapshot 静默压掉 fresh task intent。

Phase 5:改 runtime prompt / schema

改动顺序:

  1. 修改 section text。
  2. 区分 model-facing output contract 和 schema admission policy:最终合法输出要清楚,但 schema 不要过早挡住 fallback 可修复的模型输出。
  3. 如果 required/optional 或 schema admission 变了,同步改 models.ts
  4. 同步 generated output contract。
  5. bump prompt version。
  6. regenerate assembled prompt snapshot。
  7. 跑 unit tests。
  8. 跑 real-model eval。

常用命令:

cd callytics-infrastructure

bun -e "import { buildSystemPrompt } from './lambda/contacts-analyzer/src/core/prompt-builder'; await Bun.write('./lambda/contacts-analyzer/tests/unit/__snapshots__/system-prompt.golden.txt', buildSystemPrompt());"

bun test \
  lambda/contacts-analyzer/tests/unit/models.test.ts \
  lambda/contacts-analyzer/tests/unit/prompt-builder.test.ts \
  lambda/contacts-analyzer/tests/unit/system-prompt-snapshot.test.ts \
  lambda/contacts-analyzer/tests/unit/ai-client.test.ts \
  lambda/contacts-analyzer/tests/unit/handler.test.ts \
  --timeout 120000

# 只导出 scenario review,不调用模型
npm run eval:golden -- --review-md ./task-accuracy-scenarios.md

# 真实模型 eval,需要 OPENROUTER_API_KEY
npm run eval:golden -- --runs 3

8. 未来方向:从 push context 到 pull context

一句话:

把 task 系统从「push,一次性把所有信息塞满 prompt」演进成「pull,AI 按需调 read tools 自取上下文」,并加一个独立自检 agent 评估 task 生成得对不对。

状态:

  • 方向已定。
  • 技术可行性已确认到平台层。
  • 不在当前 mega PR scope。
  • 必须等 2026-06-21-task-system-redesign-plan.md 的门禁 + 模型 B + task_sources 底座落地后启动。

硬约束:

门禁判断逻辑还错着的时候上 agent = 歪地基上盖楼。

起因

现在 contacts-analyzer 生成任务时,把 contact profile、open tasks、policy、call facts、history 一起塞进 prompt。Max 的真实怀疑是:

context 喂太多,模型反而混乱。

想要的是:

  1. 给 AI 一组只读接口 / tools,例如 read contact、read task history、read call facts、read messages。
  2. AI 自己决定需要什么 context 就调什么 tool。
  3. 另起一个只读 self-check agent,评估 task 是否生成正确,把人工 track 的工作自动化。

术语对照:

Max 的话工程术语文档对应层
一股脑塞满 promptcontext-stuffing / pushfoundation 第 2 层现状 prompt-builder
AI 自主调工具取上下文agentic / tool-calling / pullfoundation 第 2 层目标态 Context Builder
评估任务对不对的 agenteval / self-check agentEval / Replay / Observability 层

9. 技术可行性和 caveat

平台层可行:

Layer当前可用路径判断
SDKVercel AI SDK generateText({ tools }) / tool calling可做 function-calling loop
GatewayOpenRouter supports tool calling interface across supported models可作为 gateway,但具体 provider/model 要 smoke test
ModelDeepSeek API supports tool calls;OpenRouter model support 需按 model page / supported parameters 验证可行但不能只看 marketing label

重要 nuance:

  • generateObject + giant context 是 push。
  • generateText / tool loop + read tools 是 pull。
  • 迁移不是改一个参数,而是换调用模式、加 loop、定义 tool schemas、加 cost/timeout/trace。
  • 不需要从零造 agent runtime;用现有 Lambda + Vercel AI SDK loop + OpenRouter 即可试水。
  • 不需要引入 MCP。MCP 是给外部 AI client 接入;这是产品内 AI,function-calling 足够。

启动前必须做一个 staging smoke:

same model + same provider + same SDK
  -> tool call
  -> tool result
  -> final structured verdict
  -> trace captured

原因:官方文档说明机制支持 tool calling,不等于我们选定的模型 / provider / output schema 组合在多轮 loop 上稳定。

参考:

10. Self-check and pull-context agents

Self-Check Agent(独立自检 agent):先做

做什么:

task 生成后,一个只读 agent 自主调 read tools,
读取 call facts + task + contact + history,
评估这个 task 生成得对不对。

为什么先做它:

  1. 只读,不碰生产写库路径。
  2. 直接解决当前痛点:人工逐条看 task 是否对。
  3. 用低风险场景验证 tool-calling loop、trace、cost、timeout。
  4. 它属于 Eval / Replay / Observability 层,不改核心生成管道。

输出不应该直接改 task。它应该输出:

verdict: correct | suspicious | wrong | insufficient_context
expectedDecision?: structured expected decision
reason
evidenceRefs
toolTrace
cost / latency

Pull-Context Generation Agent(生成时自取上下文):后做

做什么:

contacts-analyzer 不再把所有 context push 进 prompt;
AI 先看 minimal case header,再按需调 read tools 获取 contact/task/call/message/timeline context。

为什么后做:

  1. 它动核心生成管道。
  2. 它解决“怎么取上下文”,不解决“判断逻辑对不对”。
  3. 如果门禁和 state machine 还错,pull context 只会让错误更难 debug。
  4. 先用 self-check agent 验证 loop,再上核心生成路径。

11. 顺序

1. 落地 task-system-redesign-plan
   - 门禁
   - 模型 B
   - task_sources

2. 稳定 current prompt / schema / writer / eval
   - Evidence Authority
   - Task Decision State Machine
   - taxonomy from common
   - golden eval + DB replay

3. 做 Self-Check Agent
   - read-only tools
   - no write
   - verdict only
   - trace + audit

4. 用 Self-Check Agent 的结果反哺 prompt/schema/taxonomy/eval

5. 再设计 Pull-Context Generation Agent
   - minimal initial context
   - read tools
   - loop budget
   - final structured output
   - writer unchanged / Policy Guard unchanged

CTO 判断:

  • 门禁是地基。地基错时上 agent,会把不稳定放大。
  • self-check agent 是低风险 tool-calling 试水点。
  • generation agent 动核心管道,等底座稳定再上。

12. 启动时再细化的设计

议题要定什么
Tool setAI 能读哪些 context:contact、task history、call facts、messages、timeline、taxonomy、playbook
Tool schema每个 tool 的 input / output schema、pagination、evidence refs、redaction
Permissionread-only vs write;self-check agent 只能 read
Loop controlmax steps、timeout、cost ceiling、fallback
Context policy哪些 facts 默认给,哪些必须 tool read
Observability每步调了什么 tool、读了什么、最后怎么判
Evalself-check agent 自己也要被 eval
Versioningrecipe / prompt / schema / toolset 怎么 pin

13. Recipe label mapping

recipeLabel 不是构想。traceplane 里已经有基础实现,可以作为未来 eval/agent recipe versioning 的参考。

当前行为:

  • recipes 存具体版本,主键是 project_id + target + version
  • recipe_labels 存 label 映射,主键是 project_id + target + label,值是 version
  • 运行时解析优先级:如果请求传 recipeVersion,直接按版本取;否则按 recipeLabel ?? "prod" 查 label。
  • 保存 recipe 时:POST /v1/recipes?label=prod 会保存版本,并写入 label -> version 映射。

代码位置:

traceplane/packages/core/src/reducer.ts
traceplane/packages/cloudflare/src/schema.ts
traceplane/packages/cloudflare/src/d1-store.ts
traceplane/apps/worker/src/routes/recipes.ts
traceplane/apps/worker/src/routes/runs.ts

还没产品化的部分:

  • candidate -> prod 的发布 / 晋级 workflow。
  • rollback。
  • audit UI。
  • approval policy。
  • cost / usage view。

14. 不做什么

明确排除:

  • 当前底座 mega PR 不含 agent / tool-calling 代码。
  • 不从零造 agent runtime。
  • 不引入 MCP 作为产品内 AI 的第一实现。
  • 不在门禁修复前上 Pull-Context Generation Agent。
  • 不让 self-check agent 写 production DB。
  • 不让 prompt 手写复制 taxonomy。

15. 给未来 AI agent 的执行口令

当未来 agent 被要求“提高 task prompt 准确率”时,必须先做:

1. Read this doc.
2. Read current assembled system prompt snapshot.
3. Read system prompt section files and prompt assembly order.
4. Read user prompt builder.
5. Read Zod schema.
6. Read writer / Policy Guard behavior.
7. Classify each proposed rule into:
   - system prompt
   - user prompt
   - schema
   - writer / guard
   - taxonomy
   - eval
8. If taxonomy definition is the source problem, change common taxonomy first.
9. Only then patch runtime prompt/schema.
10. Regenerate prompt snapshot and run tests/evals.

If the agent cannot explain what the model sees and which layer owns the rule, it is not allowed to patch prompt text.