[{"content":" 本文档基于实际业务的抽象基础框架，我们给他起了个名字：reins。体现\u0026quot;收敛控制权 + 预算约束\u0026quot;，谐音\u0026quot;reigns（统治）\u0026ldquo;双关。 为了不涉及具体业务，相关业务用”A业务“代替\n0. 定位澄清：规范 vs 框架，以及为什么不直接用 Eino 0.1 为什么不直接用 Eino Eino（字节跳动，Go 生态）是目前 Go 技术栈最成熟的 Agent 编排框架，与本方案最直接竞争。以下是认真评估后的结论：\nEino 擅长的部分（可以直接复用）：\n强类型 Graph/Chain 编排，利用 Go 泛型保证组件类型安全 ChatModel、Tool、Retriever 等基础组件抽象 流式输出（Streaming）管道 Eino 没有解决、也不打算解决的部分（本方案的真正价值所在）：\n问题 Eino 的立场 本方案的解法 预算治理：token/time/retry 如何跨 Agent 统一封顶？ 不内置，由调用方自行实现 WorkflowState + BudgetManager，编排层统一持有预算，禁止 Agent 私自重试 审批式 Handoff：Agent 提出的协作建议如何经过审批再执行？ 不支持，Eino 的 Handoff 是直接转交 Handoff 仅为建议，Orchestrator 审批后才执行，防环、防成本放大 会话级配置快照：热更新时如何保证同一 workflow 内行为不漂移？ 不内置，框架无配置版本化概念 config_snapshot_id 绑定到 workflow，全程读取同一快照 可观测性作为协议：UsageStats 是否强制随每次输出上报？ 可选，各组件实现程度不一 AgentOutput.Usage 是必填字段，不填则协议不完整 结论：Eino 解决的是\u0026quot;如何把 LLM 调用和 Tool 调用组合起来\u0026rdquo;，本方案解决的是\u0026quot;如何在生产中治理 Agent 的预算、控制权、配置一致性和可观测性\u0026quot;。两者不重叠，可以共存——**推荐在 Agent 内部实现中使用 Eino 的 ChatModel/Tool 抽象，而不是自建模型调用层；但 Orchestrator、WorkflowState、BudgetManager、ConfigSnapshot 这些治理机制仍需自建。\ngraph LR subgraph 自建部分 - Eino 不覆盖 Orchestrator[Orchestrator 编排引擎] WorkflowState[WorkflowState 状态机] BudgetMgr[BudgetManager 预算治理] CtxBuilder[ContextBuilder 上下文裁剪] ConfigSnapshot[ConfigSnapshot 配置快照] end subgraph 可复用 Eino 组件 EinoChatModel[Eino ChatModel] EinoTool[Eino Tool] EinoStream[Eino Streaming] end Orchestrator --\u0026gt; BaseAgent[BaseAgent 接口] BaseAgent --\u0026gt; EinoChatModel BaseAgent --\u0026gt; EinoTool EinoChatModel --\u0026gt; EinoStream 0.2 五个横切关注点的差异化评估 挑刺者指出\u0026quot;只有预算治理和可观测规范有差异化价值\u0026quot;，这个判断基本成立：\n横切关注点 成熟框架是否解决 差异化程度 结论 控制权收敛（审批式 Handoff） 部分——LangGraph 有审批节点，Eino 不支持 高：Go 生态无现成方案 必须自建 单一状态源（WorkflowState 持久化） 部分——框架提供内存状态，不负责持久化 中：持久化方案各家不同 必须自建 预算驱动执行 否——所有主流框架均不内置跨 Agent 预算治理 高 必须自建 最小上下文裁剪 部分——LangChain 有 memory 组件，但未解决摘要策略 中 自建裁剪策略 可观测强制规范（UsageStats 作为协议必填） 否——框架提供 callback，但不强制 高 协议层强制 修订后的重点：方案不回避其他三个点（它们仍然需要设计），但明确标注预算治理、审批式 Handoff、可观测规范是本方案相对成熟框架的核心差异化价值。\n1. 核心设计哲学 1.1 控制权收敛原则 Agent 只能表达意图，编排层拥有决策权。\nAgent 可以提出 handoff 建议，但不能自行移交控制权。Orchestrator 是唯一的运行时决策者。\n为什么：Agent 间直接 handoff 在原型好用，但在生产中 WorkflowState 由谁持有、重试由谁发起、防环由谁检测都会产生歧义。控制权收敛是可观测性和可恢复性的根基。\n1.2 单一状态源原则 WorkflowState 由编排层唯一持有，Agent 是无状态执行器。\n所有运行时状态在编排层落库后再触发下一跳。Agent 接受输入、返回输出，不感知也不持有全局状态。\n1.3 最小上下文原则 Agent 只获取完成当前任务所需的最小上下文，由编排层按需裁剪。\nAgent 不自拉历史，编排层构造 ContextEnvelope 时主动裁剪。上下文越大，token 成本越高，注意力干扰越强；Agent 自拉上下文则使权限边界和成本都不可控。\n1.4 预算驱动执行原则（核心差异化点） 所有资源消耗必须在启动前显式建模，不允许无上限执行。\n每个 workflow 启动时分配总预算（deadline/token/retry），编排层同时下发分片预算。预算数值不注入原始值到 Prompt，而是由 Budget Interpreter 在触阈时转译为语义指令。\n为什么这是差异化：LLM 调用 × 重试 × fallback × 修复回路的成本叠加效应极强。Eino/LangChain 等框架均不内置跨 Agent 的统一预算管控，这是本方案独立存在的核心理由之一。\n1.5 可观测优先原则（核心差异化点） 可观测性是协议的一部分，而不是事后插件。\nAgentOutput.Usage 是必填字段，缺失则协议不完整。TraceContext 的 6 个字段必须在所有内部调用中强制透传。\n2. 整体架构 graph TD subgraph 接入层 API[API / CLI / IM] end subgraph 编排层 Orchestration Layer Router[意图路由 Intent Router] Orchestrator[编排引擎 Orchestrator] CtxBuilder[上下文构造器 ContextBuilder] BudgetMgr[预算管理器 BudgetManager] StreamAssembler[流式事件组装器] end subgraph Agent 层 AgentA[Agent A] AgentB[Agent B] AgentN[Agent N ...] end subgraph 工具与执行层 ToolRegistry[工具注册表 ToolRegistry] Tool1[Tool 1] Tool2[Tool 2] end subgraph 模型层 LLM Infra LLMGateway[模型网关 LLMGateway] SecFilter[安全过滤器] Models[(LLM / Embedding)] end subgraph 旁路系统 ConfigService[配置服务 Config Snapshots] ObsSystem[可观测性 OTel / Metrics] EvalSystem[评测系统 Eval] end subgraph 状态存储 Redis[(Redis 热状态)] DB[(DB 审计持久化)] end API --\u0026gt; Router Router --\u0026gt; Orchestrator Orchestrator --\u0026gt; CtxBuilder CtxBuilder --\u0026gt; BudgetMgr Orchestrator --\u0026gt; AgentA Orchestrator --\u0026gt; AgentB Orchestrator --\u0026gt; AgentN AgentA --\u0026gt; ToolRegistry AgentB --\u0026gt; ToolRegistry ToolRegistry --\u0026gt; Tool1 ToolRegistry --\u0026gt; Tool2 AgentA --\u0026gt; LLMGateway AgentB --\u0026gt; LLMGateway Orchestrator --\u0026gt; LLMGateway LLMGateway --\u0026gt; SecFilter SecFilter --\u0026gt; Models Orchestrator --\u0026gt; Redis Orchestrator --\u0026gt; DB Orchestrator --\u0026gt; StreamAssembler StreamAssembler --\u0026gt; API ConfigService -.-\u0026gt;|快照| Orchestrator ConfigService -.-\u0026gt;|快照| AgentA ObsSystem -.-\u0026gt;|Trace| EvalSystem 3. 核心抽象层设计 字段标注说明：[通用] 表示与业务无关、可直接复用；[A业务] 表示包含业务假设，其他项目需根据自身场景调整或删除。\n3.1 BaseAgent 接口 // AgentDescriptor 描述 Agent 能力，供意图路由和编排层使用 type AgentDescriptor struct { Name string // [通用] 唯一标识 Description string // [通用] 能力描述（用于 LLM 路由） InputTypes []string // [通用] 接受的 artifact 类型 OutputTypes []string // [通用] 产出的 artifact 类型 // [通用] 可选元数据，业务层通过 tags 携带自定义属性（如 risk_level、domain 等） // 不在接口层内置业务字段，避免通用协议被业务假设污染 Tags map[string]string } // BaseAgent 是所有 Agent 的统一接口 type BaseAgent interface { Run(ctx context.Context, input ContextEnvelope) (AgentOutput, error) Describe() AgentDescriptor } 关于 RiskLevel 的修订：初稿中 AgentDescriptor.RiskLevel 是 A业务 特有的安全分级概念，不应内置到通用接口层。改为通过 Tags[\u0026quot;risk_level\u0026quot;] 携带，业务层自行解读；不需要此字段的项目不受影响。\n设计原则：\n单一入口：Run 是唯一执行入口，防止编排层绕过协议调用内部方法 无状态约定：Run 不依赖实例内部状态，同等输入在任意实例上产生等价输出 可扩展元数据：Tags 替代内置业务字段，接口稳定，业务语义在调用方自行解释 3.2 意图路由协议（RouteDecision） // [通用] type RouteDecision struct { TargetAgent string // 主路由目标 Agent 名称 SecondaryHints []string // 接力意图声明（Orchestrator 消费，不透传 Agent） RouteReason string // 路由原因，用于审计 Confidence string // \u0026#34;high\u0026#34; | \u0026#34;medium\u0026#34; | \u0026#34;low\u0026#34; RuleID string // 命中的规则 ID，空字符串表示 LLM 兜底 ModelVersion string // LLM 路由时使用的模型版本 IsFallback bool // 是否为兜底路由 } 关键设计决策：\nSecondaryHints 仅供 Orchestrator 消费，不透传给 Agent Confidence 是 Orchestrator 的风险信号：high 直接执行，medium 激活结果验证钩子，low 保守路由 RuleID + ModelVersion + Confidence 三元组是路由层可追溯性基础 路由策略（两级）：\nflowchart LR Q[用户请求] --\u0026gt; R1{规则匹配} R1 --\u0026gt;|命中| D[RouteDecision confidence=high] R1 --\u0026gt;|未命中| LLM[LLM 分类] LLM --\u0026gt;|置信 medium/high| D LLM --\u0026gt;|置信 low| Fallback[默认 Agent 兜底 or 用户补充输入] Fallback --\u0026gt; D 3.3 工作流状态模型（WorkflowState） WorkflowState 是系统唯一运行时状态源，由编排层持有和持久化。\ntype WorkflowStatus string // [通用] 核心状态 const ( StatusPending WorkflowStatus = \u0026#34;pending\u0026#34; StatusRouted WorkflowStatus = \u0026#34;routed\u0026#34; StatusRunning WorkflowStatus = \u0026#34;running\u0026#34; StatusWaitingHandoff WorkflowStatus = \u0026#34;waiting_handoff_approval\u0026#34; StatusWaitingUserInput WorkflowStatus = \u0026#34;waiting_user_input\u0026#34; StatusPartialFailed WorkflowStatus = \u0026#34;partial_failed\u0026#34; StatusCompleted WorkflowStatus = \u0026#34;completed\u0026#34; StatusFailed WorkflowStatus = \u0026#34;failed\u0026#34; StatusCanceled WorkflowStatus = \u0026#34;canceled\u0026#34; ) // [通用] WorkflowState 核心字段 type WorkflowState struct { WorkflowID string // 全局唯一 SessionID string // \u0026#34;tenant:user:session\u0026#34; TenantID string // 租户隔离键 Status WorkflowStatus CurrentAgent string // 当前执行中的 Agent VisitedAgents []string // 已访问 Agent 列表（防环检测） StepIndex int // 当前步骤序号 DeadlineMs int64 // 工作流总截止时间戳（Unix ms） RetryBudget int // 剩余重试次数 TokenBudget int // 剩余 token 预算 ConfigSnapshotID string // 绑定的配置快照（会话内不变） ArtifactRefs []string // 已产出产物引用 TraceID string // 贯穿全链路的 Trace ID // [扩展点] 业务层可通过此字段携带自定义状态，框架不感知 Extensions map[string]interface{} } 关于 needs_manual_review 状态的修订：初稿将其内置为通用状态，但这是 A业务 表达式必须人工审核的业务约束，不应内置在通用状态机中。修订方案：通用状态机只内置上述 9 个状态；\u0026ldquo;等待人工审核\u0026quot;通过两种机制支持——①业务层在 Extensions 中携带标记；②Orchestrator 提供 ReviewHook 扩展点（见第 5.1 节），业务层注册后在合适时机触发。\n状态机转换图：\nstateDiagram-v2 [*] --\u0026gt; pending pending --\u0026gt; routed : 路由成功 pending --\u0026gt; failed : 路由失败 routed --\u0026gt; running : 开始执行 running --\u0026gt; waiting_handoff_approval : Agent 提出 handoff waiting_handoff_approval --\u0026gt; running : Orchestrator 批准 waiting_handoff_approval --\u0026gt; failed : 拒绝且无可用 Agent running --\u0026gt; waiting_user_input : 需要用户补充信息 waiting_user_input --\u0026gt; running : 用户补充完成 waiting_user_input --\u0026gt; canceled : 超时或用户取消 running --\u0026gt; partial_failed : 非关键步骤失败 partial_failed --\u0026gt; completed : 有可返回结果 partial_failed --\u0026gt; failed : 无可用结果 running --\u0026gt; completed : 成功完成 running --\u0026gt; failed : 执行失败 note right of running 业务层可通过 ReviewHook 在此状态挂起等待人工确认 end note 3.4 Agent 输入协议（ContextEnvelope） // [通用] type ContextEnvelope struct { TaskInstruction string // 针对当前 Agent 的具体任务指令 ConversationExcerpt []ConvTurn // 最近 N 轮原文（N 由 ContextBuilder 策略决定） ConversationSummary string // 更早轮次的摘要（摘要策略见 5.2 节） Artifacts []ArtifactRef // 上游结构化产物引用 SessionMeta SessionMetadata Budgets ExecutionBudget ExecutionHints *ExecutionHints // 可选，Orchestrator 按需填充 } // [通用] type ExecutionBudget struct { DeadlineMs int64 TokenBudget int } // [通用] 执行提示，帮助 Agent 感知自身在 pipeline 中的位置 // 注意：这里使用开放字符串而非枚举，避免框架预设 pipeline 形态 type ExecutionHints struct { // [通用] Agent 在当前调用链中的角色描述，影响输出详细程度 // 示例值：\u0026#34;first_in_chain\u0026#34; | \u0026#34;last_in_chain\u0026#34; | \u0026#34;standalone\u0026#34; | 业务自定义值 // 非线性 pipeline 的项目可使用不同的语义，框架不做强制约束 PipelineRole string // [通用] 下游期望的产物类型，引导 Agent 在非终止节点时输出结构化数据 DownstreamExpects string // [通用] 仅用于调试和可观测性，Agent 不应基于此做决策 ChainIntent string } 关于 PipelineRole 的修订：初稿将 first_in_chain | last_in_chain 作为枚举写死，隐含了\u0026quot;线性 pipeline\u0026quot;假设。修订为开放字符串，框架不约束取值——线性 pipeline 项目用 first/last_in_chain，DAG 型项目可自定义语义（如 parallel_branch）。\n预算注入策略：\n条件 注入行为 token_budget \u0026lt; 阈值（如 800） 追加语义指令：response_constraint: 资源受限，给出简洁直接的回答 deadline_ms \u0026lt; 阈值（如 3000ms） 追加语义指令：response_constraint: 时间受限，跳过深度检索 均宽松 不注入任何内容 Runtime 层 始终设置 max_tokens + HTTP timeout 作为硬约束，与 Prompt 无关 3.5 Agent 输出协议（AgentOutput） // [通用] type AgentOutput struct { ProtocolVersion string // 协议版本 Status string // \u0026#34;success\u0026#34; | \u0026#34;partial\u0026#34; | \u0026#34;error\u0026#34; Answer string // 面向用户的文字答案 Artifacts []Artifact Handoff *HandoffSuggestion // 可选，仅为建议 Confidence ConfidenceSignal Usage UsageStats // [通用] 必填，缺失视为协议不完整 Error *AgentError NextAction *NextAction // 面向用户的下一步提示 } // [通用] type Artifact struct { ID string Type string // 业务层定义类型标签 Content json.RawMessage // 见下方说明 Metadata ArtifactMeta } // [通用] 必填 type UsageStats struct { InputTokens int OutputTokens int ToolCalls int LatencyMs int64 } 关于 Artifact.Content interface{} 的修订：\n初稿用 interface{}，ProtocolVersion 在序列化后形同虚设——反序列化时仍需 type switch，版本字段无法保护结构变化。\n修订方案：将 Content 改为 json.RawMessage，配合 Type 字段实现按类型分发反序列化：\n// 消费方按 Type 分发，得到强类型结构 switch artifact.Type { case \u0026#34;feature_list\u0026#34;: var fl FeatureList json.Unmarshal(artifact.Content, \u0026amp;fl) case \u0026#34;code_snippet\u0026#34;: var cs CodeSnippet json.Unmarshal(artifact.Content, \u0026amp;cs) } 版本演进机制的真实保护：ProtocolVersion 保护的是外层信封结构（AgentOutput 的字段集合），而不是每个 Artifact 的内容结构。Artifact 内容结构的版本由 ArtifactMeta.ContentVersion 字段管理：\ntype ArtifactMeta struct { Version string // Artifact 内容结构版本（如 \u0026#34;v1\u0026#34;, \u0026#34;v2\u0026#34;） Source string CreatedAt time.Time } 版本演进规则：\n外层信封（AgentOutput 字段集）：新增字段向后兼容，删除/重命名字段升 ProtocolVersion Artifact 内容结构：新增字段向后兼容，破坏性变更升 ArtifactMeta.Version，消费方按 version 分支处理 新 Artifact 类型：只新增 Type 枚举值，老消费方收到未知 type 时静默忽略 这是软约定而非硬保护——框架层无法在编译期阻止消费方忽略版本字段。工程团队需通过 review 规范和集成测试来保证版本演进不破坏兼容性。\n4. 关键机制设计 4.1 编排机制 模式：Supervisor 主控 + 审批式 Handoff\nsequenceDiagram participant U as 用户 participant R as Intent Router participant O as Orchestrator participant A1 as Agent A participant A2 as Agent B U-\u0026gt;\u0026gt;R: 用户请求 R-\u0026gt;\u0026gt;O: RouteDecision O-\u0026gt;\u0026gt;O: 构造 WorkflowState \u0026#43; ContextEnvelope O-\u0026gt;\u0026gt;A1: ContextEnvelope (with budgets) A1--\u0026gt;\u0026gt;O: AgentOutput (handoff.target=AgentB) O-\u0026gt;\u0026gt;O: 审批 handoff：校验环路 \u0026#43; 预算 \u0026#43; 策略 O-\u0026gt;\u0026gt;A2: ContextEnvelope (carry_artifacts) A2--\u0026gt;\u0026gt;O: AgentOutput (status=success) O--\u0026gt;\u0026gt;U: 聚合结果 \u0026#43; 流式事件 Handoff 通用约束：\nAgent 只能提出建议，Orchestrator 审批后才执行 禁止 A → B → C → A 循环（通过 visited_agents 检测） Orchestrator 拒绝 handoff 时，使用当前最优结果兜底 关于\u0026quot;最多一次 Handoff\u0026quot;的标注：初稿将此作为通用限制，但这是 A业务 第一阶段的业务约束（防止在 3 个 Agent 场景中链路过长），而非框架级规则。通用框架层提供 MaxHandoffDepth 配置项，默认值由业务层设置：\n// [通用] Orchestrator 配置 type OrchestratorConfig struct { // 最大 handoff 深度，0 表示不限制 // A业务 V1 推荐设为 1，其他场景可调整 MaxHandoffDepth int // 其他配置... } ReviewHook 扩展点（替代内置 needs_manual_review 状态）：\n// [通用扩展点] 业务层注册，在 Agent 输出满足特定条件时触发人工审核流程 type ReviewHook interface { // ShouldReview 判断当前输出是否需要人工确认 ShouldReview(output AgentOutput, state WorkflowState) bool // OnReviewRequired 触发时的处理逻辑（如更新 Extensions、发送通知） OnReviewRequired(ctx context.Context, output AgentOutput, state *WorkflowState) error } A业务 的表达式人工审核通过注册此 hook 实现，而非内置在通用状态机中。\n4.2 上下文管理机制 graph LR FullHistory[(完整会话历史 DB)] ActiveCtx[(活跃会话 Redis)] CtxBuilder[ContextBuilder] Envelope[ContextEnvelope] FullHistory --\u0026gt;|历史摘要| CtxBuilder ActiveCtx --\u0026gt;|最近 N 轮| CtxBuilder CtxBuilder --\u0026gt;|裁剪 \u0026#43; 组装| Envelope Envelope --\u0026gt;|下发| Agent 裁剪策略（默认）：\n内容 策略 最近 2 轮对话 原文保留 2 轮以前的历史 摘要替代（策略见下） 上游 Artifacts 按 ID 引用或按 type 裁剪 错误/诊断日志 仅保留结构化摘要，不把完整 stderr 塞入 Prompt RouteDecision.* 完全不进入 Prompt 摘要策略（补充初稿的留白）：\n早期对话历史用摘要替代，但\u0026quot;摘要怎么做\u0026quot;是上下文管理中最容易出错的部分。提供两种默认实现供选择：\n方案 实现 优点 缺点 适用场景 A: LLM 摘要 调用轻量模型对历史对话生成摘要 语义保真度高，不丢关键信息 引入额外 token 消耗和延迟 对话轮次多、上下文依赖强的场景 B: 规则截断 保留最近 K 轮，超出则丢弃 零成本、零延迟、结果确定 早期关键信息可能丢失 轮次少、任务相对独立的场景 A业务 推荐选 B（规则截断）：A业务 Agent 的每次 workflow 通常独立性较强（问答/特征搜索/表达式生成各自一个任务），跨 workflow 的长期记忆依赖度低，不值得为摘要引入额外的 LLM 调用成本。\n摘要接口（支持扩展）：\n// [通用扩展点] 业务层可自定义摘要策略 type SummaryStrategy interface { Summarize(ctx context.Context, turns []ConvTurn, budget TokenBudget) (string, error) } // [通用] 默认实现：规则截断（保留最近 K 轮，其余丢弃） type TruncateSummary struct{ KeepLastK int } // [可选] LLM 摘要实现，需业务层自行注入 LLMClient type LLMSummary struct{ Client LLMClient; MaxSummaryTokens int } ContextEnvelope → Prompt 渲染策略：\n[System] {agent_role_description} 当前租户：{tenant_id} ← 来自 SessionMeta，业务层按需注入 [User] \u0026lt;context_envelope version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;conversation_excerpt\u0026gt;...\u0026lt;/conversation_excerpt\u0026gt; \u0026lt;artifacts type=\u0026#34;{artifact_type}\u0026#34;\u0026gt;...\u0026lt;/artifacts\u0026gt; \u0026lt;execution_hints\u0026gt; pipeline_role: {role} downstream_expects: {type} \u0026lt;!-- 触阈时追加 response_constraint --\u0026gt; \u0026lt;/execution_hints\u0026gt; \u0026lt;task_instruction\u0026gt; {具体任务指令，放在最后，优先级最高} \u0026lt;/task_instruction\u0026gt; \u0026lt;/context_envelope\u0026gt; 4.3 预算治理机制（核心差异化点） graph TD WorkflowBudget[WorkflowBudget 总预算] StepBudget[StepBudget 分片预算] BudgetInterpreter[BudgetInterpreter] RuntimeHardLimit[Runtime 硬约束 max_tokens \u0026#43; HTTP timeout] PromptHint[Prompt 语义约束 response_constraint] WorkflowBudget --\u0026gt; StepBudget StepBudget --\u0026gt; BudgetInterpreter BudgetInterpreter --\u0026gt; RuntimeHardLimit BudgetInterpreter --\u0026gt; PromptHint 预算分配规则：\n预算类型 分配方式 说明 deadline_ms 按步骤比例分配，保留 buffer 总 deadline 减已消耗，按剩余步骤均分 token_budget 按 Agent 角色分层设置 路由 \u0026laquo; 问答生成 \u0026lt; 代码生成/修复 retry_budget 按资源类型分开计数 禁止叠加放大，Agent 不能私自无限重试 预算耗尽处理：\ndeadline_ms 耗尽：返回当前最优结果或 partial 状态 token_budget 耗尽：停止工具调用，用已有信息生成简洁回答 retry_budget 耗尽：返回最后一次结果或错误，不继续重试 4.4 工具注册与调用机制 // [通用] type Tool interface { Name() string Description() string InputSchema() Schema Execute(ctx context.Context, input map[string]interface{}) (interface{}, error) } // [通用] type ToolRegistry interface { Register(tool Tool) error Get(name string) (Tool, error) ListFor(agentName string) []Tool // 按 Agent 权限返回可用工具列表 } 工具调用原则：\n工具通过注册表统一管理，Agent 不直接实例化工具依赖 工具执行必须继承 ContextEnvelope.Budgets 的超时约束 工具调用失败返回结构化错误，不允许 panic 或静默失败 工具调用次数计入 AgentOutput.Usage.ToolCalls 4.5 LLM 网关抽象 // [通用] 屏蔽厂商差异 type LLMClient interface { Chat(ctx context.Context, req ChatRequest) (ChatResponse, error) ChatStream(ctx context.Context, req ChatRequest) (\u0026lt;-chan ChatDelta, error) Embed(ctx context.Context, texts []string) ([][]float32, error) } // [通用] type ChatRequest struct { Messages []Message Tools []ToolSchema MaxTokens int Temperature float32 ModelHints ModelHints } // [通用] 用意图表达偏好，网关决定实际路由 type ModelHints struct { Tier string // \u0026#34;economy\u0026#34; | \u0026#34;standard\u0026#34; | \u0026#34;premium\u0026#34; UseCase string // \u0026#34;generation\u0026#34; | \u0026#34;embedding\u0026#34; | \u0026#34;rerank\u0026#34; | 业务自定义值 // [说明] \u0026#34;classification\u0026#34; 是 Router 特有场景，其他场景不一定需要 // UseCase 为开放字符串，不强制枚举，各业务按实际模型分层定义 } 为什么用 Tier 而非具体模型名：硬编码模型名会使切换模型需要修改 Agent 代码。用 Tier 表达意图，网关负责实际路由，Agent 对模型版本迭代免疫。\n与 Eino 的关系：此接口可以直接用 Eino 的 ChatModel 实现，不需要重建模型调用层。\n4.6 可观测性机制（核心差异化点） 全链路 Trace 字段（所有内部调用强制透传）：\n// [通用] 必须贯穿全链路 type TraceContext struct { TraceID string // 全链路追踪 SessionID string WorkflowID string TenantID string AgentName string // 当前执行节点 ModelName string // 实际调用的模型 ConfigSnapshotID string // 绑定的配置快照版本 } 核心指标维度：\n维度 关键指标 路由层 路由准确率、LLM 兜底率 执行层 P95/P99 延迟、Agent 错误率、handoff 触发率、fallback 触发率 成本层 token 消耗（按 tenant/agent/model 分组）、工具调用次数 质量层 置信度分布、partial 完成率 流式事件协议：\n// [通用] type StreamEvent struct { EventID string `json:\u0026#34;event_id\u0026#34;` WorkflowID string `json:\u0026#34;workflow_id\u0026#34;` Seq int `json:\u0026#34;seq\u0026#34;` // 严格递增，用于乱序检测 EventType string `json:\u0026#34;event_type\u0026#34;` Timestamp time.Time `json:\u0026#34;timestamp\u0026#34;` Payload json.RawMessage `json:\u0026#34;payload\u0026#34;` // 与 Artifact.Content 保持一致，用 RawMessage Final bool `json:\u0026#34;final\u0026#34;` } // [通用] 内置事件类型 const ( EventWorkflowAccepted = \u0026#34;workflow.accepted\u0026#34; EventRouteResolved = \u0026#34;route.resolved\u0026#34; EventAgentStarted = \u0026#34;agent.started\u0026#34; EventTextDelta = \u0026#34;text.delta\u0026#34; EventArtifactReady = \u0026#34;artifact.ready\u0026#34; EventUserInputRequired = \u0026#34;user_input.required\u0026#34; EventWorkflowCompleted = \u0026#34;workflow.completed\u0026#34; EventWorkflowFailed = \u0026#34;workflow.failed\u0026#34; // [扩展] 业务层可自定义事件类型，前缀用业务域名隔离，如 \u0026#34;A业务.expr.validation.completed\u0026#34; ) 4.7 安全机制 四层安全模型：\ngraph TD Input[用户输入] --\u0026gt; InputFilter[输入过滤 Prompt Injection 检测] InputFilter --\u0026gt; TenantIsolation[租户隔离 tenant_id 强制透传] TenantIsolation --\u0026gt; AgentExec[Agent 执行] AgentExec --\u0026gt; OutputFilter[输出过滤 敏感信息检测] OutputFilter --\u0026gt; User[用户] 安全层 机制 标注 输入侧 Prompt Injection 检测、危险关键词拦截 [通用] 租户隔离 tenant_id 贯穿路由/检索/上下文/审计 [通用] 执行侧 工具白名单、工具调用鉴权 [通用] 输出侧 敏感数据泄漏检测 [通用] 代码执行安全 沙箱隔离、函数白名单 [A业务] 仅表达式场景需要 5. 扩展点设计 // [通用] 编排层扩展点接口集合 type OrchestratorExtensions struct { // 上下文摘要策略 SummaryStrategy SummaryStrategy // 人工审核钩子（替代内置 needs_manual_review 状态） ReviewHook ReviewHook // Handoff 审批策略（允许业务层自定义审批逻辑） HandoffApprover HandoffApprover // 结果聚合策略（多 Agent 输出如何合并为最终答案） ResultAggregator ResultAggregator } // [通用] Handoff 审批策略接口 type HandoffApprover interface { Approve(suggestion HandoffSuggestion, state WorkflowState) (bool, string) } 扩展规则：\n新 Agent：实现 BaseAgent，注册到编排层，无需修改编排逻辑 新 Tool：实现 Tool，注册到 ToolRegistry 新路由规则：通过 ConfigCenter 热更新，无需重启 新 Artifact 类型：新增 Type 枚举值，老消费方收到未知 type 时静默忽略 模型切换：修改网关路由配置，Agent 代码零感知 人工审核：注册 ReviewHook，而不是修改状态机 摘要策略：注入 SummaryStrategy 实现，支持 LLM 摘要或规则截断 6. 配置治理 配置分类与治理策略 配置类别 内容 真源 热更新 路由规则 关键词匹配、LLM 模型选择 Git 是（读新快照） Prompt 模板 System Prompt、Instruction 模板 Git 是（读新快照） Agent 策略 timeout/retry/token budget Git 是（读新快照） 模型路由 模型分层映射 Git 是（读新快照） 会话级配置快照：\nWorkflow 启动时绑定当前 config_snapshot_id，全程读取同一快照 回滚时按 snapshot 版本回滚，而非直接改线上值 这一机制是框架独立存在的核心价值之一（Eino 不内置此能力） 7. 评测与发布门禁 三层质量门禁 graph LR Change[Prompt/规则/代码变更] --\u0026gt; G1[Golden Set 回归] G1 --\u0026gt;|通过| G2[Trace Replay / Shadow] G2 --\u0026gt;|通过| G3[小流量灰度] G3 --\u0026gt;|指标稳定| FullRollout[全量发布] G1 --\u0026gt;|失败| Reject[拒绝发布] G2 --\u0026gt;|失败| Reject G3 --\u0026gt;|指标越界| AutoRollback[自动回滚] 评测闭环：\nBad Case 回灌：线上失败样本、误路由样本进入评测集 评测结果驱动优化，不靠直觉改 Prompt 各层评测指标 评测对象 核心指标 意图路由 路由准确率、LLM 兜底率 Agent 质量 任务完成率、置信度分布、partial 率 工具调用 工具成功率、平均调用次数 成本 每次 workflow 平均 token 消耗 后记 我认为：在多 Agent 的生产系统里，可控性是效率的前提，而不是效率的对立面。\n不可控的自治，最终会把工程师的时间消耗在排查\u0026quot;AI 为什么这么做\u0026quot;而不是\u0026quot;让 AI 做得更好\u0026quot;上。\n在设计一个 Agent 系统，先不要问\u0026quot;哪个框架最强大\u0026rdquo;，先问两个问题：出了问题，你能解释清楚它为什么这么做吗？预算耗尽了，有人喊停吗？\n这两个问题的答案，决定你的架构选型。\n","permalink":"https://www.chunshuijiancha.top/posts/llm/agent/base-agent-reins/","summary":"很多人以为构建 Agent 系统，最难的问题是\u0026quot;让 LLM 更聪明\u0026quot;。真正进入生产环境，你会发现把你逼疯的不是模型效果，而是模型的“不确定性”。","title":"reins框架：把Agent驾驭的”缰绳“握在自己手里"},{"content":" 通过 SDD + OpenSpec，我们在一个真实的存量项目中，用 AI 生成了 9600+ 行可编译、可测试的代码，手动修改不到 50 行。本文记录了完整的实践过程、评测结果和踩坑经验。\n什么是SDD 从 Vibe Coding 说起 2024年底，Andrej Karpathy 提出了一个概念——Vibe Coding（氛围编程）：\n\u0026ldquo;你完全沉浸在\u0026rsquo;氛围\u0026rsquo;中，拥抱指数级增长，忘记代码的存在。就是跟AI对话、让它干活、看看结果、反复迭代。\u0026rdquo;\n在2025年，我也在多种场景下使用 Vibe Coding，但实际企业项目更注重确定性、规范性、可维护性，小范围的 Vibe Coding 确实有效——比如：明确函数定义后补充实现、编写单测等。但一旦把范围扩展到复杂需求、项目重构时，就失效了。也尝试过编写面向场景的 Agent，但结果还是差强人意。\n反复碰壁后，我总结出 Vibe Coding 在企业项目中失败的三个根本原因：\n上下文丢失（Lost in the Middle）：复杂任务需要多轮对话，但随着对话轮次增加，AI 开始”遗忘”前面的约定——前后矛盾、反复纠正，效率急剧下降。 领域知识缺失：复杂项目往往有复杂的业务背景、技术约定和关联知识（如内部框架的使用规范、团队的分层架构约定等），纯对话的方式很难将这些隐性知识完整地传递给 AI。 缺乏结构化约束：AI 每次生成的代码风格、架构决策不一致。简单功能还好，复杂逻辑频繁出错，调 Bug 的时间甚至超过手写。项目规模越大，”改一处坏十处”的问题越严重。 归结为一句话：Vibe Coding 给了 AI 自由度，却没给它约束。\nSDD的定义与核心思想 Spec-Driven Development（SDD） 是2025年兴起的一种通过结构化规范文档驱动AI生成代码的开发范式。\n核心理念：\n人定义 \u0026#34;What\u0026#34;（做什么）和 \u0026#34;Constraints\u0026#34;（约束条件） AI负责 \u0026#34;How\u0026#34;（怎么实现） 如果做一个类比：\n范式 类比 特点 Vibe Coding 跟AI口头聊需求 自由但不可控 SDD 给AI一份详细的设计说明书 有约束但可控 SDD之于AI编程，类似于TDD之于传统开发——都是通过”先定义预期”来约束”实现过程”。\nSDD 中的 “S” 怎么理解 对照熟悉的TDD、BDD，SDD 的 S（Spec） 本质上是一份给 AI 的完整工作说明书，它比 TDD 的测试用例更宽（不仅定义验证标准，还定义架构约束、技术选型、编码规范），比 BDD 的 Feature 文件更深（不仅描述业务行为，还包含实现层面的技术约束）。\n三者是传承演进关系，而非互相替代：\nTDD 说的是”代码该怎么跑”（面向机器验证） BDD 说的是”功能该怎么表现”（面向人机共识） SDD 说的是”AI 该怎么干活”（面向 AI 约束） 💡 SDD 可以利用 TDD、BDD 进一步明确需求，消除歧义和不确定性。实际上，OpenSpec 的 spec.md 中 Requirement + Scenario 的写法，正是 BDD 的影子。\n什么场景适合引入 SDD SDD 不是银弹，它有明确的适用范围（详见文末”能力边界”章节）。简单判断：\n✅ 值得用：需求可以被结构化描述、项目有明确架构规范、团队多人协作 ❌ 不必用：探索性原型、一次性脚本、小范围 hotfix 📌 核心判断标准：如果你能写清楚”做什么”和”约束是什么”，SDD 就能帮你；如果需求本身都说不清，先理清需求再说 SDD 带来的核心价值 从本质上看，SDD 属于 Context Engineering（上下文工程） 的范畴。如果说 Prompt Engineering 关注的是”怎么问 AI 一个好问题”，那么 Context Engineering 关注的是”怎么给 AI 构建一个好的工作环境”。SDD 正是通过结构化的 Spec 提供丰富、准确、持久的上下文，使 AI 在更受控的条件下工作。\nVibe Coding 痛点 SDD 的解法 延伸价值 上下文丢失 Spec 文件持久化上下文，不依赖对话记忆 可追溯性：需求 → 规范 → 代码，每一步有据可查 领域知识缺失 config.yaml + specs/ 注入领域知识 团队协作：Spec 成为团队共识的载体 缺乏结构化约束 Spec 定义明确的输出边界 可控性 + 可复现性：相同 Spec 产出一致质量的代码 选型 / 业界有哪些解决方案 目前业界和司内有不少SDD实践，主要有三个 SDD 方案值得关注：OpenSpec、Spec-Kit 和 Kiro。\n方案一览 维度 OpenSpec Spec-Kit Kiro 作者 Fission AI（社区） GitHub（官方） AWS（官方） 形态 CLI 工具 + AI 斜杠命令 CLI 工具 + AI 斜杠命令 独立 IDE + CLI 运行时 Node.js ≥ 20.19 Python 3.11+ / uv 独立应用（VS Code 兼容） 设计哲学 事务性 Delta（只记变更差异） 宪法治理（constitution.md 约束全局） 集成式 Spec（Prompt → Spec → 代码一站式） 工作流 3 步循环：Proposal → Apply → Archive 6 阶段流水线：Init → Constitution → Spec → Plan → Tasks → Implement 线性流：需求描述 → 结构化 Spec → 架构设计 → 任务拆解 → Agent 实现 规范格式 自定义 Markdown（Requirement + Scenario） 每阶段独立文档 EARS 表示法（Event-Action-Response-State） Agent 支持 12+ 原生斜杠命令（Claude Code、Cursor 等） 25+（几乎所有主流 Agent） 仅限 Kiro 内置 Agent 扩展生态 精简，无扩展体系 丰富，40+ 社区扩展 内置 Agent Hooks + MCP 集成 Token 效率 ⭐⭐⭐ 高（Delta 传输） ⭐ 低（全量规范） ⭐⭐ 中（IDE 自行管理上下文） 上手成本 ⭐ 低 ⭐⭐ 中等 ⭐ 低（IDE 自带引导） 锁定风险 低（标准 Markdown 文件） 低（标准文件 + Python 工具） 高（绑定 Kiro IDE） 适用场景与局限性 方案 最佳场景 局限性 OpenSpec 🟢 存量项目持续迭代（Brownfield）；希望在现有 IDE/Agent 中使用 SDD 社区较小，治理能力弱，需团队自律维护 Spec Spec-Kit 🟢 新项目从零构建（Greenfield）；大团队需要强治理和流程规范 流程较重，简单需求也要走完整流水线；Python 依赖 Kiro 🟢 个人/小团队全新项目；希望开箱即用的一站式体验 绑定 Kiro IDE，无法在 Cursor/VS Code/Claude Code 中使用；Spec 变更分散在多个文件夹，不如 OpenSpec 按 Feature 聚合 我们的选择 本文选择 OpenSpec 作为重点介绍对象，原因：\n我们的场景以存量项目迭代为主，OpenSpec 的 Delta 模型更契合 不绑定特定 IDE，可与 Claude Code、codebuddy 等自由组合 Token 效率高，在大型项目中优势明显 💡 三者并非对立关系。理论上可以用 Spec-Kit 做项目初始化建立治理规范，后续用 OpenSpec 做持续迭代。\nOpenSpec 深度解析 架构 OpenSpec 采用双目录模型，核心思想是将\u0026quot;正式规范\u0026quot;与\u0026quot;变更提案\u0026quot;显式分离：\nopenspec/ ├── specs/ # 📚 正式规范（Source of Truth） │ └── [domain]/ │ └── spec.md # 某领域的完整规范 ├── changes/ # 📝 变更提案（进行中的修改） │ └── [feature-name]/ │ ├── proposal.md # 变更提案：做什么、为什么 │ ├── tasks.md # 实现任务列表 │ ├── design.md # （可选）设计文档 │ └── specs/ │ └── [domain]/spec.md # Delta：只包含本次变更的差异 ├── archive/ # 📦 已完成的变更归档 └── config.yaml # 🤖 项目上下文信息，会主动注入到每个 OpenSpec 规划请求中 关键设计：\nDelta而非全量：changes/ 中的 spec 文件只包含 ADDED、MODIFIED、REMOVED 的部分，而非完整规范的副本。这大幅减少了token消耗。\n按 Feature 组织变更：每个feature一个文件夹，包含完整的提案-任务-差异链路，便于追溯。\n使用流程 OpenSpec 最新的工作流引擎叫 OPSX，它与传统瀑布式流程有本质区别：\n传统瀑布流 OPSX 推进方式 阶段锁定，必须按顺序完成 “Actions, not phases”，满足前置条件即可执行任意动作 迭代成本 回到上一阶段代价高 随时修改proposal，更新specs，自然迭代 产物粒度 所有文档一次性生成 可逐个生成spec（原子化），也可批量生成 核心命令 日常使用只需 3 个命令：\n命令 作用 说明 /opsx:propose 提案 创建变更 + 规划产物（proposal、tasks、spec delta） /opsx:apply 实施 按 tasks 执行实现，逐个标记完成 /opsx:archive 归档 将 delta 合并到正式 specs，标记变更完成 想更细粒度控制？/opsx:explore仔细思考，明确需求；/opsx:continue（逐个产物推进）和 /opsx:ff（一键快进全部规划产物）灵活切换。\n“快进”与”原子化”的平衡 这是 OPSX 最实用的设计——根据你对需求的信心程度选择推进速度：\n信心高、scope 清晰 → /opsx:ff 一键快进，批量生成所有规划产物，最大化效率 需要逐步验证 → /opsx:continue 一次生成一个产物，审查通过后再推进下一个 需求还在摸索 → /opsx:explore 先自由探索，想清楚了再进入结构化流程 实际工作中，三种模式经常交替使用：先 explore 理清思路，propose 后 ff 快速生成规划，apply 阶段发现问题则回头修改 spec 重新 apply——不是线性流水线，而是有状态感知的灵活迭代。\n实际使用 ⚠️ 以下案例为实际项目中的使用体验，包含真实的需求、生成结果和评测。\n需求1：推荐分发服务升级（分发字段管理部分） 需求实现 需求描述： 推荐分发服务，需要将分发的字段管理起来，记录每个订阅方使用了哪些字段。“分发字段管理”功能，涉及13个接口，5张数据表。\nSpec文件编写：\n如下初始的propose：\n验证过程中发现，还是有部分功能缺失，直接在 tasks.md 中添加“修正说明”，然后让AI进行修复。\n生成代码展示：\n总共有两次MR，总共新增了 9600+ 行代码，我自己手动修改的 \u0026lt; 50 行（小调整通过spec描述，还不如直接修改高效），可以算是“全部代码由AI生成”。\n下图是第一次propose生成的代码，新增7000+行，代码能编译通过，测试可以通过。\n结果评测 评测维度说明：\n代码正确性：功能是否按需求正确实现，边界条件是否处理 代码规范性：是否遵循项目编码规范，命名、格式、注释是否合理 可维护性：代码结构是否清晰，是否易于理解和修改 性能：是否存在明显的性能问题，如N+1查询、不必要的计算 安全性：是否存在SQL注入、XSS等安全漏洞 测试覆盖：生成的代码是否包含或易于编写单元测试 评测维度 实际结果（10制） 差距分析 解决方案 功能完整性 完整度高，未随意发散，接口可调通（9分） 缺少一定的”智慧”，未描述清楚的部分，没有基于”逻辑”补充完整。比如，不应该支持多人同时提交编辑，Spec中未强调，实现就未做对应限制 在 Spec 中补充隐含的业务规则和边界条件 代码规范性 符合规范（9分） 1）缺少包注释；2）有一个函数超过80行。不符合”公司代码规范” 在 config.yaml 中注入公司编码规范 可维护性 较高（8分） 有2个大函数和部分重复代码 Spec 中增加函数复杂度约束（如单函数不超过80行） 性能 中等（9分） 未明确说明并行，但多个独立数据源，可并发请求 在 framework-conventions.md 中说明 trpc.GoAndWait 用法 安全性 未发现安全问题（10分） 无安全问题 — 测试覆盖 单测覆盖率约50%，单测可跑通（9分） 单测可跑通，单测覆盖率70% Spec 中明确要求测试覆盖率目标 遇到的典型问题 问题1：缺少内部框架知识 分析：此场景可以使用trpc.GoAndWait()简化代码，但是公开 LLM 的训练数据不包含企业内部框架。\n可行的方案：\n在 specs/ 中建立一份 framework-conventions.md，描述框架的关键约定 提供典型的代码示例作为参考（few-shot），而非仅靠文字描述 【建议】长期来看，可以考虑用 RAG 或 MCP 工具将内部框架文档接入 AI 上下文 问题2：缺少代码分层和架构规范知识 分析：缺少接口协议层（用于转换内外部协议、实现协议防腐）。AI 生成的代码只关注功能实现，不考虑代码架构。\n可行的方案：\n提供分层模板：在 specs/ 中维护一份 architecture.md，描述项目的分层架构、各层职责边界、典型文件结构 在 Spec 中显式声明分层要求： 技术约束： - 必须遵循三层架构：Service（协议层）→ Logic（业务层）→ Repository（数据层） - Service 层负责内外部协议转换，Logic 层不应直接感知外部协议格式 - 所有跨层数据传递通过 DTO 进行，禁止直接透传 Protobuf 结构体 问题3：Spec编写的粒度 分析：Spec写太粗，AI自由发挥空间过大，产出不可控；Spec写太细，相当于在用自然语言\u0026quot;写代码\u0026quot;，失去了AI提效的意义。\n建议：\n接口层面：定义清楚入参、出参、异常情况 业务规则：用 Scenario 格式明确各场景预期行为 技术约束：明确技术栈、框架版本、编码规范 不要规定：具体的实现步骤、变量命名、代码行数 💡 Scenario 格式与 BDD 的关系：这里的 Scenario（WHEN\u0026hellip;THEN\u0026hellip;）本质上就是 BDD（Behavior-Driven Development）中 Feature → Scenario 的简化写法。BDD 强调用\u0026quot;行为描述\u0026quot;定义预期，SDD 继承了这一思想——用 Scenario 描述 Feature 在各种条件下的预期行为，作为 AI 生成代码的验收标准。可以说，SDD 的 Spec 就是 BDD 的 Feature 文件的演进形态，从\u0026quot;给测试框架读\u0026quot;变成了\u0026quot;给 AI 读\u0026quot;。\n需求2：字段转换协议调整 需求实现 需求描述：\n字段转换关系原来通过两个参数 convert_type 和 expr 描述，现在需要新增 “func_name” 参数（转换函数），因此希望将 3 个参数收归到一个结构体中（破坏性更新，删除旧字段）。\nSpec 编写：\n第一次编写的 Spec（未明确说明删除旧字段，AI采用了向后兼容模式）：\n第二次编写的Spec（明确删除旧字段，并手动更新了pb和db schema）：\n生成代码展示：\n需求完整度很高，我没有做任何调整，直接进行了提交\n结果评测 相比需求1（大需求、13个接口），需求2 是一个较小的重构需求。AI 生成的代码完整度很高，无需手动修改，直接提交。但也暴露了两个值得注意的问题（见下文）。\n遇到的典型问题 问题1：AI理解与预期不一致 分析：AI 默认考虑“向后兼容”，实现本身没有问题，但是需求希望是“破坏性更新”，需要删除“convert_type”和“expr”\n可行的方案：\n提供最新的约束：【建议】提供最新的pb协议、db schema，或其他依赖资源，以免AI随意发挥 Spec中明确说明：spec中明确说明删除旧字段（往往我们是以为很清楚了，实际 别人/AI 不一定清楚） 问题2：单测增量覆盖率不够 分析：Spec中并没有明确要求单测覆盖率，所以默认没有新增单测导致覆盖率不够\n可行的方案：\n在 CLI 中补充增量覆盖率要求 在 config.yaml中补充单测覆盖率要求，作为项目要求 总结与建议 SDD 的能力边界 诚实地讨论一下 SDD 适合和不适合的场景：\n✅ 适合使用 SDD 的场景 场景 原因 需求明确的功能开发 Spec 可以清晰定义预期行为，AI 产出质量高 CRUD 和标准业务逻辑 模式化程度高，Spec → Code 转化率高 存量项目的功能迭代 Delta 模型天然适合增量开发 有明确架构规范的项目 约束越清晰，AI 产出越可控 多人协作项目 Spec 成为团队共识载体，减少沟通成本 ❌ 不适合使用 SDD 的场景 场景 原因 探索性原型/POC 需求本身不明确，写 Spec 的成本 \u0026gt; 直接写代码的成本 高度创新性的算法 AI 对创新算法的理解有限，Spec 也难以精确描述 性能极致优化 涉及底层优化、汇编级调优，需要深度专业知识 遗留代码的紧急修复 小范围 hotfix 不值得走完整 SDD 流程 强依赖内部框架且无文档 AI 无法理解未文档化的内部框架，Spec 再详细也无用 🟡 可以但需谨慎的场景 场景 建议 复杂业务逻辑 SDD + 人工 Review 双重保障，不能盲目信任 AI 产出 涉及安全的模块 用 SDD 生成初版，但必须经过安全专家审查 跨系统集成 Spec 中需详细描述外部系统的接口契约和异常处理 📌 判断标准：如果你能用 Spec 清晰描述\u0026quot;做什么\u0026quot;和\u0026quot;约束条件\u0026quot;，SDD 就能帮你提效；如果连需求本身都说不清楚，那应该先花时间理清需求，而非急于使用任何工具。\n对开发者的启示 —— 回归业务本质 从 IaaS、PaaS、SaaS、FaaS，到 AIaaS（Artificial Intelligence as a Service），所谓”平权”也好，所谓”基础设施”也好，作为软件工程师都更聚焦于“业务/商业”的本质。\n也引出另一个问题：编程的本质是什么？ 编程从来不只是”写代码”。代码只是思想的载体，是业务规则的具象化。编程的本质是将模糊的现实问题转化为精确的逻辑表达——理解需求、抽象建模、设计边界、处理异常、权衡取舍。这些才是软件工程师真正的核心价值。 写 Spec 的过程，就是在做问题定义、架构决策和边界判断；审查 AI 产出的过程，就是在做质量把控；维护 Spec 的过程，就是在做知识沉淀。\n工程师的核心价值 具体表现 AI 能否替代 问题定义 从模糊的业务需求中提炼出精确的技术问题 ❌ 不能 架构决策 在多种方案中基于团队现状、系统约束做取舍 ❌ 不能 边界判断 识别哪些是关键约束、哪些可以妥协 ❌ 不能 质量把控 审查产出是否符合工程标准和业务预期 🟡 部分 知识沉淀 将隐性经验转化为可传承的规范和文档 🟡 部分 OpenSpec 使用建议 提供最新的依赖资源：pb 文件、db schema 等，约束越清晰明确，生成代码越准确 善用 config.yaml：手动将 pb 文件路径、db schema 等依赖信息添加到 openspec/config.yaml 中，AI 每次都能自动读取 小改动不必走 SDD：较小的改动仍建议直接手动修改（后续积累的 spec 足够多，也许可以通过”简短描述”直接修改） 展望 SDD的演进方向 与CI/CD深度集成：未来Spec不仅驱动代码生成，还可以驱动测试生成、文档生成、甚至部署配置。Spec成为整个研发流水线的\u0026quot;源头活水\u0026quot;。\nMulti-Agent协作的基石：在Multi-Agent架构中，Spec文档可以作为多个Agent之间的\u0026quot;协议\u0026quot;——Code Agent根据Spec写代码，Test Agent根据Spec写测试，Review Agent根据Spec做审查，保证各Agent行为一致。\nSpec的自动演化：当前Spec需要人来维护更新。未来可能出现\u0026quot;Spec Agent\u0026quot;，根据代码变更自动更新Spec，实现Spec与代码的双向同步。\n行业标准化：目前OpenSpec和Spec-Kit各有格式标准。随着SDD被更多团队采用，社区可能会收敛出统一的Spec格式标准，类似于OpenAPI之于REST API。\nSDD + TDD：传承与发展：SDD 并非对传统工程实践的否定，而是传承和发展。在 SDD 工作流中，完全可以结合 TDD——先从 Spec 生成测试用例（作为验收标准），再让 AI 生成实现代码，最后用测试验证代码正确性。这形成了 Spec → Test → Code 的闭环：Spec 定义\u0026quot;做什么\u0026quot;，Test 定义\u0026quot;怎么验\u0026quot;，AI 负责\u0026quot;怎么做\u0026quot;。TDD 为 SDD 的产出提供了自动化的质量保障网，两者结合比单独使用任何一个都更可靠。\n写在最后 SDD 不是终点，而是 AI 辅助研发走向工程化的一个关键节点。从 Vibe Coding 的自由探索，到 SDD 的规范约束，我们正在找到\u0026quot;AI自由度\u0026quot;与\u0026quot;工程可控性\u0026quot;之间的平衡点。\n这次初体验的核心收获：AI 的能力上限取决于你给它的约束质量。写好 Spec，比写好 Prompt 重要得多。\n本文是 SDD 系列的第一篇，记录了\u0026quot;从零到跑通\u0026quot;的初体验。后续我们会继续深入实践，计划探索的方向包括：\nSDD + TDD 的结合实践：用 Spec 驱动测试生成，再用测试验证代码，形成完整闭环 大型项目的 Spec 治理：当 specs/ 目录膨胀后，如何组织和维护 团队协作中的 SDD 落地：如何在团队中推广 SDD，建立 Spec Review 机制 config.yaml 的最佳实践：如何把内部框架知识、架构规范高效注入 AI 上下文 下一篇：OpenSpec 架构：把 AI 协作从“对话驱动”变成“状态驱动\n","permalink":"https://www.chunshuijiancha.top/posts/llm/xdd/openspec/part-1-onboarding/","summary":"通过 SDD + OpenSpec，我们在一个真实的存量项目中，用 AI 生成了 9600+ 行可编译、可测试的代码，手动修改不到 50 行。本文记录了完整的实践过程、评测结果和踩坑经验。","title":"从 Vibe Coding 到 SDD：企业级 AI 编程范式的演进与实践"},{"content":" 写个Skill 一天就能上线，但生产级的Skill你会发现远没有那么简单：Skill 设计不是 prompt 工程，而是一门系统工程。\n读完你会得到：\n一个三层架构范式，让 Prompt、适配逻辑、后端服务各司其职 一套实现方式选型决策树，告诉你什么时候该用 CLI、什么时候才需要 MCP 五要素安全防护体系，专门针对 AI 写操作的风险边界 一张可直接使用的 Skill 设计检查清单 先说局限：本文经验来自 databus_debug_toolkit ，场景特征是小规模用户、内部系统、调试导向。如果你的 Skill 面向外部用户、有高 SLA 要求，本文是参考案例，不是银弹。\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n先定义\u0026quot;高质量\u0026quot;，再动手 \u0026ldquo;高质量\u0026quot;不是感性词汇。对于生产级 AI Skill，它的评价维度有四个，且存在真实权衡：\n维度 含义 与其他维度的权衡 安全可控 写操作不会静默执行，敏感数据不会泄漏 安全越高，摩擦越大 可审计 所有操作有完整日志，可追溯、可归因 审计越全，存储成本越高 用户体验 低延迟、友好的错误提示、合理的权限边界 体验越好，往往需要更多工程投入 可演进 能阶段化推进，解决当前主要矛盾，保留升级扩展能力 灵活性越高，初期架构设计越复杂 这四个维度不能同时最优化。你必须排序。\n本文的优先级是：安全可控 \u0026gt; 可审计 \u0026gt; 可演进 \u0026gt; 用户体验。这是内部工具的合理排序。如果你做的是面向 C 端的 Skill，体验的优先级要大幅提升，但本文不覆盖那个场景。\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n三层架构：让每一层只做一件事 Skill 架构最容易被忽视的设计原则是职责分层。\n┌─────────────────────────────────────────┐ │ Prompt 层（AI 交互层） │ │ · 工具定义（名称/描述/参数 Schema） │ │ · 意图澄清与错误引导 │ │ · 安全约束（写操作双确认提示） │ └─────────────────┬───────────────────────┘ │ 标准化调用契约 ┌─────────────────▼───────────────────────┐ │ 适配层（协议桥接层） │ │ · MCP Server（HTTP/stdio） │ │ · 内联 Markdown（静态知识注入） │ │ · 审计日志 \u0026amp; 可观测性 │ └─────────────────┬───────────────────────┘ │ 服务调用（gRPC/HTTP/SDK） ┌─────────────────▼───────────────────────┐ │ 服务层（业务逻辑层） │ │ · 数据查询 / 写操作执行 │ │ · 鉴权 \u0026amp; 频率限制 │ │ · 审计日志 \u0026amp; 可观测性 │ └─────────────────────────────────────────┘ 三层各有一个核心职责，不能混：\nPrompt 层是与 LLM 直接交互的契约层。核心原则是\u0026quot;工具描述即文档\u0026rdquo;——description 字段不能省略、不能模糊；参数必须有明确的类型约束；错误路径需要预设引导语，而非留给模型自由发挥。一个写得好的 Tool description，是 AI 能正确调用工具的基础。\n适配层是变化最快、选型差异最大的层。它的核心职责只有一个：将 AI 的结构化调用翻译为下游服务能理解的协议。不承载任何业务逻辑——否则逻辑分散，难以测试。\n服务层是纯粹的业务逻辑层，对 AI 的存在无感知。这层的 API 必须能被工程师直接调用和测试，而不是只能通过 AI 触达。这保证了可测试性和故障的可归因性。\n💡 对于审计、可观测性等应该放在服务层还是适配层？ 对于新服务，建议放在“服务层”，职责更加明晰，而且这些功能对普通API服务也是必须的；对于旧服务来说，可完全不用改造，可以将审计、可观测等放在适配层即可\n职责混淆是 Skill 腐化的第一步。\n最常见的反模式：把频率限制逻辑放在 prompt 里，比如\u0026quot;请不要在循环中批量调用\u0026quot;。这依赖模型的自觉，不是架构约束。安全约束必须在适配层或服务层技术强制实现——写在 SKILL.md 里的规则，AI 可以忽视；写进代码里的限制，AI 绕不过去。\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n架构选型：别一上来就 MCP 直觉上，MCP 是\u0026quot;现代的、结构化的、为 AI 设计的\u0026quot;，应该是第一选择。我们最初也这么想。在实际对比 14 个维度之后，发现这个直觉是错的。每种模式都有适用的场景。\n三种实现方式的核心差异：\n方式 核心机制 优点 缺点 典型场景 内联 Markdown Skill（Skill + Function Call） SKILL.md 提示词 + AI 通过 Function Call 调 HTTP API 零改造成本、学习成本低、快速迭代 重用性低、难以测试、AI 行为有歧义、凭证管理困难 快速实验、只读查询、低风险操作 Skill + MCP 将服务改造为 MCP Server，AI 调用标准化工具 工具语义明确、安全集中管控、跨项目复用、可观测性好 需改造服务、引入运维成本、团队需了解 MCP 协议 跨项目复用、写操作、企业级治理 Skill + CLI 封装为命令行工具，Skill 通过 Bash 调用 可独立测试、与 DevOps 工具链集成 分发成本高、认证分散、交互能力弱 CI/CD 集成、批处理、脚本化 选型决策树：\n是否有明确的 MCP Server Owner（被纳入服务治理）？ ├── 否 → 内联 Markdown Skill，仅覆盖只读操作（schema 查询等） └── 是 ├── 需要「有状态会话」或「细粒度权限控制」？ │ ├── 是 → MCP（HTTP 模式） │ └── 否 │ ├── 工具链是否已有成熟 CLI？ │ │ ├── 是 → CLI 封装（利用现有生态） │ │ └── 否 → 评估冷启动延迟 │ │ ├── 延迟敏感（\u0026lt;200ms 体感阈值）→ CLI 或进程常驻 │ │ └── 延迟不敏感 → MCP（更好的类型安全） │ └── 需要跨工具共享状态？ │ ├── 是 → MCP │ └── 否 → CLI（更简单、更易调试） 我们为什么最终选了 MCP HTTP 模式？\ndatabus_debug_toolkit 有三个具体需求：写操作的两步确认需要在请求之间维护预览状态（Session Store）；多用户共享同一服务实例需要 appid 维度的频率限制；Phase 2+ 需要细粒度权限控制。这三个需求组合在一起，CLI 很难优雅实现，MCP HTTP 模式天然支持。\n但这不是绝对结论。 如果已有CLI，或者都是单步操作，或者利用本地资源做一些逻辑，完全建议使用CLI。\n💡 一个值得考虑的实践：在 MCP 与 CLI 之上建立统一抽象层，通过配置切换适配方式，避免将协议选择硬编码进 Prompt 层。这样从 CLI 迁移到 MCP 时，不需要修改工具描述。\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n安全设计：五要素防护体系 这是生产级 Skill 和玩具 Skill 的分水岭。\nAI 调用工具的节奏与人类完全不同。LLM 在单次 ReAct 循环中可能在毫秒内发起数十次工具调用，完全没有\u0026quot;我再想想\u0026quot;的自我审视。系统不替它做防护，就没有人替它做。\n────────────────────────────────────\n要素一：最小权限（Least Privilege） Phase 1 只开放只读接口。\n这不是懒，是用最小成本验证价值假设的工程哲学。只读操作的失败边界清晰——最坏情况是\u0026quot;返回错误结果\u0026quot;，不造成数据损坏。先上线只读工具，可以在零副作用条件下验证三件事：工具描述的准确性、AI 调用的参数合法性、下游服务的容量压力。\n这三件事的验证数据，是写操作上线的前置信心来源。\n────────────────────────────────────\n要素二：频率限制（Rate Limiting） 技术强制，不依赖 prompt。\n频率控制必须在适配层（MCP Server）实现，不能写在 SKILL.md 里说\u0026quot;请不要批量调用\u0026quot;。\ndatabus_debug_toolkit 的配置：\nrate_limits: - tool: tools_get_full_features per_minute: 30 # 单实体查询，防批量拉取 - tool: tools_get_event_list per_minute: 20 # 支持翻页，额外降低阈值 阈值必须通过配置文件管理，不能硬编码，上线后根据实际观测到的使用模式调整。\n实现算法使用 Token Bucket（令牌桶），支持突发后平滑降速。\n────────────────────────────────────\n要素三：字段过滤（Output Field Filtering） 敏感字段的过滤在适配层统一处理。后端返回原始数据，过滤逻辑在 MCP Server 侧维护。\n一个关键的架构决策：使用白名单还是黑名单？\ndatabus_debug_toolkit 当前（Phase 1）采用黑名单——屏蔽已知敏感字段。理由是初期敏感字段边界尚未梳理清楚，白名单会导致太多合法字段被拦截。但黑名单有一个明显风险：新增字段默认可见，必须有机制保证新字段被及时评估。\n我们的对策：Phase 2 上线前完成完整的敏感字段梳理，并将其作为 Phase 2 准入标准的硬性条件。\n如果你的 Skill 面向更敏感的数据，建议从一开始就使用白名单。\n────────────────────────────────────\n要素四：写操作双确认（Two-Phase Write） 这是设计中最容易被误解的部分。\n双确认不只是 UX 设计，它是一个架构级的熔断机制。\n在 AI 意图与实际执行之间插入一个人类审核节点，确保 LLM 幻觉或参数误解的影响范围被限制在\u0026quot;预览阶段\u0026quot;。\n执行流程：\n用户自然语言 → AI → MCP Server（confirmed=false，预览模式） ↓ 返回操作预览 + 影响范围描述 AI 展示预览给用户 ↓ 用户明确确认（真人操作，不是 AI 替代） AI → MCP Server（confirmed=true，同一 session） ↓ 校验 session 内存在对应预览记录 + 参数哈希一致 → 后端 API 执行 → 写入审计日志 一个关键的安全质疑：AI 能否自动 bypass 这个确认？\n这是真实存在的风险。对策是双重防护，缺一不可：\n技术强制（第一道）：MCP Server 验证同一 session 内存在预览记录。如果 AI 直接传 confirmed=true 而没有先做预览，服务端返回 preview_required 错误，技术上拒绝执行。这不依赖 AI 的自觉。\nPrompt 约束（第二道）：SKILL.md 中明确写明\u0026quot;必须等待用户明确说\u0026rsquo;确认执行\u0026rsquo;等确认词后，才能传 confirmed=true\u0026quot;。Prompt 层是第二道防线，不是第一道。\n5 分钟预览有效期 + 参数哈希校验（防止预览后篡改参数）是额外的安全加固。\n────────────────────────────────────\n要素五：审计日志（Audit Trail） 审计日志只有被消费才是安全能力。否则只是存储成本。\n我们的设计：所有写操作产生 AUDIT 级别日志，保留 ≥30 天，接入公司内部可观测系统。告警规则包括：\nwrite_op_skip_preview_total \u0026gt; 0（任意 1 分钟内）→ P1 告警，立即审查 单 appid 1 小时内响应数据条数 \u0026gt; 1000 → P2 告警，检查批量爬取 工具 3 分钟错误率 \u0026gt; 10% → P2 告警 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n阶段化演进：晋级标准是硬条件，不是感觉 不要一上来就搞大而全的tool，可以对使用方进行调研，对高频使用的只读操作，优先提供。 对企业场景来说，安全可用永远是第一位的。\n我们演进的三步节奏：\nPhase 1：只读 + 单工具验证 ↓ （观测调用模式、收集错误样本、建立安全基线） Phase 2：组合工具 + 可逆写操作（双确认） ↓ （验证副作用边界、完善审计体系） Phase 3+：不可逆操作 + 批量写操作 （依赖外部审批流，安全评审通过后） ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n我们踩过的坑 一篇没有失败的技术分享，读起来像成功学。这里列出我们实际遇到的四个问题。\n────────────────────────────────────\n坑 1：Tool description 写得不够精确\n早期版本的 tools_get_full_features description 只写了\u0026quot;查询实体的完整特征数据\u0026quot;。\n结果：AI 在不需要全量数据的场景下也会调用这个工具，且不使用 focus_keys 参数，每次返回大量无用字段，既浪费 token，又触发了数据量告警。\n修复：在 description 中明确写\u0026quot;可使用 focus_keys 缩小返回字段范围以控制数据量。注意：本接口有频率限制，请勿在循环中批量调用。\u0026ldquo;并在 inputSchema 中给 focus_keys 加上使用场景描述。\n💡 教训：description 字段不是备注，是给 LLM 看的调用契约。模糊的描述等于把调用策略的决定权交给模型。\n────────────────────────────────────\n坑 2：缺乏多维度的观测数据\n最初只有请求量和请求成功率。\n后来发现，根本不知道系统被谁访问了，是否\n────────────────────────────────────\n坑 2：频率限制的粒度设计\n最初想按 session_id 做频率限制——同一会话内限流。\n后来意识到这不能防止攻击者开多个会话绕过限制，应该按 appid 限流（同一 appid 跨会话累计计数）。\n💡 教训：限流的粒度要和攻击面对齐。session 级别的限流防不住\u0026quot;多开会话\u0026quot;的绕过。\n────────────────────────────────────\n坑 4：错误信息透传\n早期版本直接把后端 gRPC 错误原样返回给 AI，包含服务名、内部错误码等敏感信息。在一次内部安全 review 时被指出。\n修复：在适配层统一做错误过滤，将后端原始错误映射为用户友好的描述，原始错误只写入日志。\n💡 教训：适配层的职责之一就是信息过滤。后端的错误细节是内部信息，不应该直传到 AI 上下文。\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSkill 设计检查清单 把上面的经验提炼成一张自检清单，直接使用。\n价值验证（开始动手前） 明确目标用户是谁，他们会在什么场景下选择用 Skill 而非直接操作 量化\u0026quot;成功\u0026quot;的指标（调用量？排障时间缩短？原始接口访问减少？） 确认是否有 Owner 负责运维和安全响应 架构设计 三层职责是否清晰（Prompt / 适配 / 服务） 适配方式选型（内联 Markdown / CLI / MCP）是否有明确的决策理由 安全约束是否在适配层技术强制，而非仅依赖 prompt Prompt 层 每个 Tool 的 description 是否足够精确（可做 AI 可用性测试验证） 参数 Schema 是否有合法取值说明、上下限约束 错误路径是否有预设的用户引导语 安全设计 威胁模型是否明确（防谁、防什么、不防什么） 高风险接口是否有频率限制（技术强制） 字段过滤策略（白名单/黑名单）是否明确，变更机制是否确定 写操作是否有两步确认，且第二步需要人工触发（不可被 AI 自动 bypass） 审计日志是否有消费侧（谁看、触发什么告警） 演进策略 Phase 1 是否只开放只读操作 Phase 2 的晋级标准是否可量化（不是\u0026quot;感觉稳了\u0026rdquo;） MCP Server 接口是否只增不改（向后兼容约束） 上线前 内部用户灰度验证（至少 2 人，至少 3 天） 接入内部可观测系统（指标上报 + 结构化日志） 告警规则配置完成（包括写操作绕过预览的 P1 告警） ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n结语 永远不要相关AI，即使99次都正常，也不能保证第100次一定正常。但是作为系统负责人，你是需要对系统、数据、安全负责的。\n所以，把 Skill 当成系统工程来设计，加好“harness”。\n","permalink":"https://www.chunshuijiancha.top/posts/llm/agent/production-grade-skill/","summary":"写个Skill 一天就能上线，但生产级的Skill你会发现远没有那么简单。Skill 设计不是 prompt 工程，而是一门系统工程","title":"AI Skill 不是写个 Prompt 那么简单"},{"content":"OpenSpec 架构 整体架构 可以把它理解成三层：\n状态层：仓库目录本身就是状态存储 计算层：schema + dependency graph + validator 交互层：CLI 和 skills，把结构化状态喂给 AI 或人 这套分层很重要，因为它避免了一个常见问题：把“流程”硬编码进 prompt。在 OpenSpec 里，prompt 只是表现层；真正的工作流定义在 schema 中，真正的状态在文件系统里。\n核心对象 OpenSpec 其实有4个核心对象：\nspecs：当前系统行为的 source of truth changes：待合入的变更包 schema：工作流定义，决定有哪些 artifact、它们如何依赖 archive：变更完成后的历史沉淀 如果第一次使用，你可能会好奇schema是什么?\n在每次变更文件夹中，.openspec.yaml文件中就有 schema字段，默认值为spec-driven。可以在源码中，看到spec-driven定义了产出物（artifacts）的目录、文件名、文件格式、生成Prompt等，以及实施阶段（apply）读取的任务文件和Prompt。 这是OpenSpec预定义的schema，我们也可以自定义schema，对他进行扩展。\n工作流 如下是OpenSpec默认的工作流，通过schema定义中的requires字段定义依赖关系。\nflowchart LR proposal --\u0026gt; specs proposal --\u0026gt; design specs --\u0026gt; tasks design --\u0026gt; tasks tasks --\u0026gt; apply apply --\u0026gt; archive artifacts: - id: proposal generates: proposal.md requires: [] - id: specs generates: specs/**/*.md requires: [proposal] - id: design generates: design.md requires: [proposal] - id: tasks generates: tasks.md requires: [specs, design] apply: requires: [tasks] 这里的设计哲学：dependency is an enabler, not a gate。 也就是说，依赖表示“现在什么是可做的”，而不是“你必须严格按唯一顺序前进”。这和很多传统 spec 流程差异很大：后者把流程建模成 phase，前者把流程建模成 action + dependency。\n这是一个非常适合 AI 协作的建模方式，因为真实开发本来就不是线性的。你今天写完 design，明天发现 assumptions 错了，回头改 spec，这很正常。OpenSpec 没有试图消灭这种回流，而是承认它。\n扩展性好。可以很方便的扩展自定义的artifacts 比如，希望添加“test_case”编写环境，只需要按照已有artifacts格式，定义清楚“requires”、“instruction”等信息即可\n状态机：blocked / ready / done 以默认schemaspec-driven为例，执行 opsx:propose命令后，artifacts依赖关系如下图：\nflowchart TD A[proposal] --\u0026gt; B[specs] A --\u0026gt; C[design] B --\u0026gt; D[tasks] C --\u0026gt; D 那他们是是怎么驱动的呢？ OpenSpec 的状态模型定义了 3 个主状态：\nstateDiagram-v2 [*] --\u0026gt; ready: 无依赖或依赖已满足 ready --\u0026gt; done: 产出文件存在 [*] --\u0026gt; blocked: 依赖未满足 blocked --\u0026gt; ready: 缺失依赖补齐 一句话，status 决定该不该做。 也就是，specs和design 状态都为done，那么“tasks”才可以执行。从而实现了“状态驱动”。 这里有一个很值得注意的设计选择：OpenSpec 的状态不是“保存在某处”，而是“计算出来的”。这意味着不需要额外的状态存储\n这种推导模式，适合轻量状态，也不用考虑状态一致性问题。不太适合复杂的过程型状态，比如，审批流。 动态指令（Prompt）生成：CLI 不是“命令行外壳”，而是 AI 的结构化上下文提供者 在很多工具里，AI 拿到的是一段固定 prompt；但 OpenSpec 的 openspec instructions 做了更有意思的一层：定义标准结构体ArtifactInstructions，根据当前 change 的实时状态，生成一个包含依赖、模板、规则、输出位置的结构化指令包，然后以 JSON 输出给 AI。\n如下为源码中Artifact Instructions定义。源码路径：src/core/artifact-graph/instruction-loader.ts\n/** * Enriched instructions for creating an artifact. */ export interface ArtifactInstructions { /** Change name */ changeName: string; /** Artifact ID */ artifactId: string; /** Schema name */ schemaName: string; /** Full path to change directory */ changeDir: string; /** Output path pattern (e.g., \u0026#34;proposal.md\u0026#34;) */ outputPath: string; /** Artifact description */ description: string; /** Guidance on how to create this artifact (from schema instruction field) */ instruction: string | undefined; /** Project context from config (constraints/background for AI, not to be included in output) */ context: string | undefined; /** Artifact-specific rules from config (constraints for AI, not to be included in output) */ rules: string[] | undefined; /** Template content (structure to follow - this IS the output format) */ template: string; /** Dependencies with completion status and paths */ dependencies: DependencyInfo[]; /** Artifacts that become available after completing this one */ unlocks: string[]; } 定义了几类信息：\n当前 artifact 是什么 输出文件应该写到哪里 依赖 artifact 是否已完成 需要先读哪些文件 当前 schema 下的模板内容 项目级 context 和 artifact 级 rules 完成该 artifact 后会解锁哪些下游 artifact 换句话说，OpenSpec 的 CLI 在做的不是“执行命令”，而是在做workflow context serving。\n这很重要，因为 AI 最怕两件事：\n看不到当前状态 看不到当前动作的边界 而 status + instructions 组合，本质上就是把这两个问题结构化了。\n项目特有约束：context 和 rule context：项目级、全局共享的背景信息，会注入到所有 artifact 的 instructions。适合放所有 artifact 都需要知道的项目事实和长期约束。 当前context限制：\n只支持字符串 支持多行 支持特殊字符 最大 50KB 太大会被忽略并报警告 rules：artifact 级、按类型区分的附加规则，只会注入到匹配的 artifact，比如只对 design 或 tasks 生效。\ncontext 和 rules 该怎么分工？一个简单判断方法： 放进 context 的内容 满足下面任一条件，就更适合放 context：\n所有 artifact 都应该知道 是稳定的项目事实 是跨阶段都适用的团队约束 更像“项目背景”而不是“这份文档怎么写” 典型内容： 技术栈 目录结构 架构风格 测试/可观测性/安全基线 术语和业务边界 团队通用编码规范 放进 rules 的内容 满足下面任一条件，就更适合放 rules：\n只针对某一类 artifact 是输出格式或内容检查点 是“proposal 应该写什么 / design 必须说明什么 / tasks 如何拆” 典型内容： proposal 必须写回滚影响 specs 必须用 SHALL/MUST design 必须解释边界与迁移 tasks 必须拆到可执行、可验证 OpenSpec是workflow 以 “opsx:propose” 为例。/opsx:propose 负责“编排流程”，依次执行下面的命令：\nopenspec new change \u0026quot;\u0026lt;name\u0026gt;\u0026quot;，创建新的 change openspec status --change \u0026quot;\u0026lt;name\u0026gt;\u0026quot; --json，计算“下一个生成的 artifact 是谁”？ openspec instructions \u0026lt;artifact-id\u0026gt; --change \u0026quot;\u0026lt;name\u0026gt;\u0026quot; --json，生成 artifact instructions “opsx:propose” 依次生成4个artifact：proposal、specs、design、tasks\nflowchart TD A[proposal] --\u0026gt; B[specs] A --\u0026gt; C[design] B --\u0026gt; D[tasks] C --\u0026gt; D 二、给我们的启示 2.1 把状态外化到仓库，而不是埋在会话里 这是 OpenSpec 最强的一点。\n设计 解决了什么 启示 文件系统承载状态 状态可见、可复算、可进入 Git 历史 对 AI 工作流来说，外显状态比“更聪明的 prompt”更重要 specs / changes 分离 当前事实与待变更解耦，支持并行变更 不要让“讨论中的方案”直接污染 source of truth schema 驱动 artifact graph 工作流可定制，不必改代码 把流程定义数据化，比把流程写死在代码里更能长期演进 delta spec 合并 对 brownfield 修改极友好 在存量系统里，差量建模通常比全量建模更实用 CLI 生成结构化 instructions 给 AI 明确边界和上下文 AI 工具真正需要的不是更多 prompt，而是更好的上下文接口 2.2 “state-first”，而不是“spec-first” 很多人会把 OpenSpec 看成 spec-first 工具，但我觉得更准确的说法是：它是一个 state-first 的 AI 协作框架。\n原因很简单：\nspec 只是 artifact 之一 ，design、tasks、proposal 也都是 artifact 真正驱动流程的不是文档内容本身，而是artifact 之间的依赖关系和当前完成态 这带来一个很实用的启示：\n对 AI 协作流程来说，最值钱的不是“把文档写标准”，而是“把下一步动作计算出来”。\n一旦系统能稳定回答下面两个问题，协作效率就会明显上升：\n我现在处于什么状态？ 我下一步最合理的动作是什么？ OpenSpec 的 status / instructions / apply，本质上都在回答这两个问题。\n2.3 “迭代”开发，而不是“线性”开发 传统流程工具喜欢 phase gate，因为它好管理；但实际开发中，需求和理解总是在变化，可能需要不断修改设计和实现。\nOpenSpec OPSX Workflow ：\n不强调 phase 强调 action 允许在实现过程中回头改 design / spec / tasks 例子：需要修改design，并级联更新spec和task，修改对应实现\n\u0026ldquo;由于性能问题，请把我们的分页方案从‘偏移量分页’改为‘游标分页’。请执行以下步骤：1. 更新 design.md； 2. 基于新的设计，同步修改 specs 下的测试用例； 3. 更新 tasks.md，把旧的 API 修改任务替换为新的游标分页任务。\u0026rdquo;\n这是一个非常成熟的 trade-off：\n优点：贴近真实开发，尤其适合 agent + 人类的往返协作 缺点：治理更弱，流程纪律更多依赖团队共识，而不是系统强制 这也给团队设计流程一个提醒：\n越贴近真实认知过程的流程，往往越不“整齐”；但它通常更高效。\n2.4 它把“可扩展性”放在核心层，而不是集成层 系统设计中，像六边形架构、洋葱模型、DDD等，都是将“可扩展性”放在非核心层，通过“Interface”隔离。 OpenSpec 更深一层，它把扩展性放在：\n模板可扩展 工作流依赖可扩展 artifact 类型可扩展 项目规则可扩展 这比“再适配一个新的外部依赖”有含金量得多。 因为真正昂贵的从来不是接一个新入口，而是让系统核心抽象在新场景下仍成立。\n三、目前的不足 OpenSpec 的设计方向是对的，但如果站在更严格的工程视角看，它现在也有一些很明显的边界。\n3.1 done 的定义过于乐观 当前完成态基本由“产出文件是否存在”决定。\n这很优雅，但也很脆弱。因为下面这些情况都会被误判为完成：\n文件只是模板骨架，还没填实质内容 spec 已经改了，但 tasks 没刷新 design 存在，但已经和 proposal 脱节 生成了 tasks.md，但里面没有有效任务 也就是说，OpenSpec 目前更像在追踪artifact existence，而不是 artifact validity / freshness。\n如果继续演进，我认为最值得补的是一个“脏态”体系，例如：\ndone：存在且通过基础校验 stale：上游依赖变化后需要重审 invalid：结构存在但不满足 schema / validator 没有这层能力，状态驱动会很容易滑向“文件驱动”。\n实际使用中，你也会发先，如果发现代码实现不符合预期，你修改了spec，缺少一种机制同步到task。如果直接改task，task又不能反向更新spec\n3.2 依赖图解决了顺序问题，但还没解决“同步失效”问题 当前图模型非常适合表达“谁先于谁”，但不太能表达“谁因为谁变化而失效”。\n例如：\nproposal scope 变了，spec 是否应该自动标记为 stale？ spec requirement 改了，tasks 是否需要重新生成？ design 改了，apply 阶段是否应该阻塞直到任务同步？ 这类问题本质上不是 DAG 的可达性问题，而是变更传播和一致性问题。\nOpenSpec 现在已经有“dependency as enabler”，下一步更难、也更关键的是“dependency as invalidation source”。\n3.3 Delta merge 以 requirement 标题为锚点，足够实用，但不够强壮 OpenSpec 在 archive 时按 requirement block 合并，这是个很好的方向；但它目前主要依赖 requirement header 做匹配。\n这会带来几个天然风险：\nrequirement 标题改写后，merge 容易脆弱 大规模编辑时，rename / modify 冲突规则会变复杂 多个变更同时改同一 requirement，冲突解析成本会上升 从工程角度看，这其实意味着它还缺少一个更稳定的逻辑主键，例如 requirement ID。\n也就是说，现在的实现更像：\n面向人类可读性优化 还没有完全走到面向机器稳定性优化 这是合理的早期 trade-off，但如果要支撑更大规模协作，这一步迟早要补。\n3.4 文件系统状态机很轻，但并发与事务语义较弱 文件系统做状态后端的优点很多，但它的短板也很明确：\n缺少强事务边界 多人并行编辑时，冲突靠 Git，而不是工作流层解决 很难表达更复杂的审核流、审批流、租约流 archive / apply 虽然已经尽量先验证后写入，但仍是多文件操作 所以我会把 OpenSpec 看成一个单仓库、轻协调、Git 友好的工作流系统，而不是一个严肃的流程编排平台。\n这不是批评，而是边界判断。\n3.5 当前验证更偏“结构正确”，还不够“语义闭环” OpenSpec 已经有 validator，也有 verify/apply 这类动作，但从整体上看，它现在更擅长检查：\n结构是否符合模板 delta 是否合法 spec 是否能被归档合并 但它还没有真正闭环到：\nrequirement 是否被代码覆盖 tasks 是否与实现一致 变更是否满足可测试性与可验证性 spec / code / test 三者之间是否能建立稳定 traceability 而这恰恰是下一阶段最值得做的地方。\n如果未来能把 requirement、实现、测试、归档历史串成一条可追踪链路，OpenSpec 的价值会从“AI 协作工具”进一步提升成“变更控制基础设施”。\n结语 我对 OpenSpec 的整体评价依然很高，它抓住了一个非常正确的方向：\n把 AI 协作从“依赖上下文记忆”推进到“依赖可计算状态”。\n在我看来，OpenSpec 最有价值的三个架构点是：\n把工作流定义从代码里抽成 schema 把协作状态外化到仓库结构里 把 brownfield 变更建模成 delta，而不是全量重写 上一篇：从 Vibe Coding 到 SDD：企业级 AI 编程范式的演进与实践\n","permalink":"https://www.chunshuijiancha.top/posts/llm/xdd/openspec/part-2-architecture/","summary":"\u003ch2 id=\"openspec-架构\"\u003eOpenSpec 架构\u003c/h2\u003e\n\u003ch3 id=\"整体架构\"\u003e整体架构\u003c/h3\u003e\n\u003cp\u003e可以把它理解成三层：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e状态层\u003c/strong\u003e：仓库目录本身就是状态存储\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e计算层\u003c/strong\u003e：schema + dependency graph + validator\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e交互层\u003c/strong\u003e：CLI 和 skills，把结构化状态喂给 AI 或人\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这套分层很重要，因为它避免了一个常见问题：把“流程”硬编码进 prompt。在 OpenSpec 里，prompt 只是表现层；真正的工作流定义在 schema 中，真正的状态在文件系统里。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"OpenSpec 整体架构图（Excalidraw 重绘）\" loading=\"lazy\" src=\"/posts/llm/xdd/openspec/part-2-architecture/arch_overall_excalidraw.svg\"\u003e\u003c/p\u003e\n\u003ch3 id=\"核心对象\"\u003e核心对象\u003c/h3\u003e\n\u003cp\u003eOpenSpec 其实有4个核心对象：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003especs\u003c/code\u003e\u003c/strong\u003e：当前系统行为的 source of truth\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003echanges\u003c/code\u003e\u003c/strong\u003e：待合入的变更包\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eschema\u003c/code\u003e\u003c/strong\u003e：工作流定义，决定有哪些 artifact、它们如何依赖\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003earchive\u003c/code\u003e\u003c/strong\u003e：变更完成后的历史沉淀\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如果第一次使用，你可能会好奇schema是什么?\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img.png\" loading=\"lazy\" src=\"/posts/llm/xdd/openspec/part-2-architecture/arch_01.webp\"\u003e\u003c/p\u003e\n\u003cp\u003e在每次变更文件夹中，\u003ccode\u003e.openspec.yaml\u003c/code\u003e文件中就有 \u003ccode\u003eschema\u003c/code\u003e字段，默认值为\u003ccode\u003espec-driven\u003c/code\u003e。可以在源码中，看到\u003ccode\u003espec-driven\u003c/code\u003e定义了产出物（artifacts）的目录、文件名、文件格式、生成Prompt等，以及实施阶段（apply）读取的任务文件和Prompt。\n这是OpenSpec预定义的schema，我们也可以自定义schema，对他进行扩展。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img.png\" loading=\"lazy\" src=\"/posts/llm/xdd/openspec/part-2-architecture/arch_schema_01.webp\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img.png\" loading=\"lazy\" src=\"/posts/llm/xdd/openspec/part-2-architecture/arch_schema_02.webp\"\u003e\u003c/p\u003e\n\u003ch3 id=\"工作流\"\u003e工作流\u003c/h3\u003e\n\u003cp\u003e如下是OpenSpec默认的工作流，通过schema定义中的\u003ccode\u003erequires\u003c/code\u003e字段定义依赖关系。\u003c/p\u003e\n\u003cdiv class=\"mermaid-container\"\u003e\n  \u003cdiv class=\"mermaid\"\u003e\nflowchart LR\n    proposal --\u0026gt; specs\n    proposal --\u0026gt; design\n    specs --\u0026gt; tasks\n    design --\u0026gt; tasks\n    tasks --\u0026gt; apply\n    apply --\u0026gt; archive\n  \u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eartifacts\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - \u003cspan style=\"color:#f92672\"\u003eid\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eproposal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egenerates\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eproposal.md\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erequires\u003c/span\u003e: []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - \u003cspan style=\"color:#f92672\"\u003eid\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003especs\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egenerates\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003especs/**/*.md\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erequires\u003c/span\u003e: [\u003cspan style=\"color:#ae81ff\"\u003eproposal]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - \u003cspan style=\"color:#f92672\"\u003eid\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edesign\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egenerates\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edesign.md\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erequires\u003c/span\u003e: [\u003cspan style=\"color:#ae81ff\"\u003eproposal]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - \u003cspan style=\"color:#f92672\"\u003eid\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003etasks\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egenerates\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003etasks.md\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erequires\u003c/span\u003e: [\u003cspan style=\"color:#ae81ff\"\u003especs, design]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eapply\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erequires\u003c/span\u003e: [\u003cspan style=\"color:#ae81ff\"\u003etasks]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003e这里的设计哲学：\u003cstrong\u003edependency is an enabler, not a gate\u003c/strong\u003e。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e也就是说，依赖表示“现在什么是可做的”，而不是“你必须严格按唯一顺序前进”。这和很多传统 spec 流程差异很大：后者把流程建模成 phase，前者把流程建模成 action + dependency。\u003c/p\u003e","title":"OpenSpec 架构：把 AI 协作从“对话驱动”变成“状态驱动”"},{"content":" 作为一名架构师，我经常被问到一个问题：\u0026ldquo;你的系统是怎么做到连续两年没有出过 P0/P1 级故障的？\u0026rdquo; 我们确实做了很多事情，但是一直没有进行系统的梳理，想趁此机会把思考、尝试讲出来。\n一、 前言：\u0026ldquo;零故障\u0026quot;的幸存者 我目前负责的服务，系统承载着 10w+ QPS，服务 20 多个业务场景。曾经出现过热点事件，系统流量上涨50%；也出现过爬虫流量导致大量冷数据绕过缓存层直穿 DB（缓存穿透，Cache Penetration），系统成功率骤降。\n系统经历这些\u0026quot;风暴\u0026rdquo;，都顺利挺过来了，我想真正的核心在于：我们业务盘点、预演，建立了一套能在悬崖边踩住刹车的底盘，确保了局部异常绝不会扩散为全局灾难（控制爆炸半径，Blast Radius）。\n二、 重新认知\u0026quot;稳定性\u0026quot; 1. 稳定性 vs 高可用 在工程实践中，我们经常说的是\u0026quot;三高\u0026quot;中的\u0026quot;高可用\u0026quot;，那高可用 与 稳定性是什么关系？\n高可用： 核心是\u0026quot;能不能访问\u0026quot;（如 99.99% 的 SLA）。常见思路是\u0026quot;做加法\u0026quot;，通过数据库一主多从、多 AZ 部署等物理冗余来避免单点故障（Avoid Failure），本质上是用成本（多副本）换生存。 稳定性： 核心是\u0026quot;综合表现是否可控\u0026quot;。除了能访问，还要保证 TP99 性能不劣化、数据不乱。面对洪峰和错误变更，依靠限流、熔断和业务降级（Graceful Degradation），用局部牺牲换取全局存活，这是面向失败的设计（Design for Failure），本质是一种妥协。 \u0026ldquo;高可用决定了系统在遇到硬件灾难时会不会\u0026rsquo;挂\u0026rsquo;，而稳定性决定了系统在面临流量洪峰、烂代码和复杂业务拉扯时，会不会走向\u0026rsquo;失控\u0026rsquo;。高可用可以花钱买服务器解决，但稳定性只能通过日复一日的技术敬畏心和严苛的工程治理来死守。\u0026rdquo;\n2. 破除迷思：\u0026ldquo;稳定\u0026rdquo; ≠ \u0026ldquo;不出故障\u0026rdquo; 很多工程师有一个执念，认为\u0026quot;系统永远不坏\u0026quot;才是好架构。这其实是只停留在高可用层面的执念。真正的稳定，是承认\u0026quot;失败是必然发生的常态\u0026quot;。硬件会老化，网络会抖动，人会犯错，外部依赖会宕机。 \u0026ldquo;稳定性的核心不是避免一切故障，而是控制影响的半径，将不可控的灾难，转化为可控的故障。\u0026rdquo;\n三、 他山之石：大厂稳定性理论速览 在进入实战复盘前，我们先对齐一下业界顶尖的稳定性流派，这是我们架构设计的\u0026quot;参考系\u0026quot;：\nGoogle SRE 体系（度量与文化）： 核心是 SLI/SLO 和 Error Budget（错误预算）。用软件工程的方法解决运维问题。 阿里/蚂蚁防御体系（变更与应急）： 核心是\u0026quot;发布三板斧（可灰度、可观测、可回滚）\u0026ldquo;以及\u0026quot;1-5-10 应急响应机制\u0026rdquo;。 Netflix 混沌工程（架构韧性）： 核心是主动向生产环境注入故障（Chaos Monkey），逼迫架构实现无状态和高可用。 我们可以看到每家公司稳定性建设各有侧重。阿里经常有各种活动和大促，变更频繁且流量波动大，所以更聚焦\u0026quot;发布变更\u0026quot;和\u0026quot;紧急故障处理\u0026quot;；Google 作为全球基础设施提供商，面向的是海量用户和极致的 SLA 承诺，因此更注重用数据量化稳定性目标（SLO），并通过 Error Budget 机制在\u0026quot;快速发布\u0026quot;与\u0026quot;稳定底线\u0026quot;之间建立制度约束；Netflix 则是流媒体业务，一次大规模宕机直接等于百万用户同时无法追剧，加之其微服务架构极为复杂，所以选择用混沌工程\u0026quot;以攻代守\u0026quot;——主动制造故障，逼迫每个服务都具备独立存活的能力。\n理论很丰满，但实际业务不可能照单全收。我们的取舍是：从阿里体系借鉴了\u0026quot;发布三板斧\u0026quot;和 P0/P1 分级响应机制；从 Google SRE 借鉴了 SLO 量化思路，用来和业务方对齐\u0026quot;可以接受多少不稳定\u0026quot;；混沌工程目前只在核心链路做了轻量级故障注入，尚未全面铺开。这种选择本身也是稳定性建设的一部分——用有限资源守住最重要的防线，而不是追求理论上的完美。\n四、 实战复盘： 我认为一个稳定的系统是：架构、业务和流程，共同协同的结果。\n架构是系统稳定性能力的基石，决定了系统的能力下限，它在技术层面限制了\u0026quot;最坏的情况有多坏\u0026quot;； 业务是系统稳定性能力的护城河，决定了系统降级后能否体面存活——没有业务兜底，再好的架构也可能在极端情况下给用户一个白屏； 流程是稳定性能力的纪律线，通过标准化的研发 SOP 将稳定性要求嵌入每一次变更，把\u0026quot;人为失误\u0026quot;这个最大的不确定因素收敛到最小。 五、 新挑战：AI 时代的\u0026quot;防御性编程 2.0\u0026quot; 随着 Copilot 等 AI 编码助手的普及，研发效能狂飙，但隐患也被放大了。AI 生成的代码看似逻辑通顺，却常常带有极具隐蔽性的漏洞——尤其是两类：并发竞态（Race Condition），如多协程共享状态时的数据竞争；以及分布式状态机时序错乱，如跨服务的幂等性破坏和消息乱序处理。 过去我们认为加个 Lint 和静态分析就能拦截，但这套手段对付 AI 生成的代码无异于隔靴搔痒——静态扫描根本查不出分布式状态机的死锁，它需要运行时的多节点协同才能复现。\n为了应对这些挑战，我认为稳定性建设进入了2.0阶段，核心思路是：让测试手段与问题类型精准匹配：\n针对时序与边界问题——引入契约测试（Contract Testing）与模糊测试（Fuzzing）： 契约测试用来卡死上下游服务的接口时序边界，防止 AI 生成的调用方和服务方对协议的理解产生隐性偏差；Fuzzing 则通过海量随机输入轰炸接口边界，暴露 AI 代码中潜藏的 panic 和非预期状态转移。 针对并发与运行时问题——强化静态分析与运行时检测： 引入 Race Detector（如 Go 的 -race flag）作为 CI 强制门禁，在流水线阶段拦截并发竞态；对复杂状态机逻辑，要求补充状态转换图（State Machine Diagram）作为 Code Review 的强制输入，而非只看代码。 常态化混沌工程（Chaos Engineering）兜底： 在上述手段之上，定期通过故障注入（断网、高延迟、脏数据）对整体链路做健壮性压测，验证即便 AI 代码出现非预期行为，系统的熔断和隔离机制也能兜住，不至于扩散为全局故障。 六、 结语 稳定性建设就是一个对抗熵增的过程，也是一项\u0026quot;反人性\u0026quot;的工程。\n说它\u0026quot;反人性\u0026quot;，是因为它的大多数成果都是负向的——你做了很多事，用户感知不到任何变化，这才叫成功。没有故障，没有白屏，没有等待，一切顺滑如常。稳定性工作的最高境界，就是让人感觉它从来不存在。\n坦诚地说，我也不认为现在的系统是完善的。当前还存在一些悬而未决的硬骨头：伪多活架构下数据库跨 AZ 同步的延迟问题，在极端网络抖动时 RPO（Recovery Point Objective）仍无法做到趋近于零。但\u0026quot;够用\u0026quot;与\u0026quot;完美\u0026quot;之间的取舍，本身就是稳定性建设的核心命题——把有限的资源投入到风险最高的地方，而不是追求面面俱到的理论完美。\n系统会变，业务会变，团队会变，威胁也会变。我唯一确定的是：保持对系统的敬畏心，不要在一段平稳期之后开始觉得\u0026quot;应该没问题了\u0026quot;。历史上大多数严重故障，恰恰发生在一段长期稳定之后——因为人的警惕性降低了，而系统的复杂性还在悄悄积累。\n","permalink":"https://www.chunshuijiancha.top/posts/llm/devops/relation-stability/","summary":"作为一名架构师，我经常被问到一个问题：\u0026ldquo;你的系统是怎么做到连续两年没有出过 P0/P1 级故障的？\u0026quot;。我们确实做了很多事情，但是一直没有进行系统的梳理，想趁此机会把思考、尝试讲出来。","title":"从架构到业务：支撑 10w+ QPS 系统两年\"零故障\"稳定性建设复盘"},{"content":"ing\u0026hellip;\n","permalink":"https://www.chunshuijiancha.top/posts/llm/xdd/openspec/part-3-enhancements/","summary":"\u003cp\u003eing\u0026hellip;\u003c/p\u003e","title":"openspec增强"},{"content":" 致谢 我们家主要是他妈妈在\u0026quot;管理\u0026quot;孩子的一切，我更多的是一个助手，先对妈妈的辛苦表示感谢。 \u0026ldquo;战争\u0026quot;的起源 孩子已经进入幼升小阶段，作为一个\u0026quot;普\u0026quot;父母，虽然我们也教了他一些基础，但亦不能免俗的给他报了拼音和英语辅导班。目前英语的教材是《LOOK新世纪少儿英语1》，每周一次课，会有一定的课后作业。\n每次做作业他都会比较拖沓，需要催促他好几次，可是越是催他越是不愿意做。\n跟身边的几个朋友聊天，发现他们家孩子（小学）也基本是拖到最后一刻才会完成作业。\n我在想他为什么会抗拒做作业呢？有没有什么办法可以自主的完成作业？\n孩子的心理 6岁小男孩心理特点 核心特点 人格发展：处于埃里克森人格发展的「主动对内疚」关键期，核心心理需求是获得成就感、掌控感，避免持续的挫败与否定 注意力：注意时长仅 10-15 分钟，以具体形象思维为主 学习方式：以无意注意为主、动觉型学习偏好明显、机械记忆向意义记忆过渡 关键启示 ⏱️ 注意力时间偏短，不能一次完成所有任务 🎯 需要掌控感，由他自主安排完成任务，家长只提醒，不催促 ⭐ 喜欢成就感，可以效仿补习班的徽章或金币体系 教育理念 核心理念 儿童本位，顺应发展规律\n不超前、不贪多，把 \u0026ldquo;保护学习兴趣\u0026rdquo; 放在 \u0026ldquo;知识积累\u0026rdquo; 之前\n建立「我能学会、学习很好玩」的核心信念\n学习的核心目标不是提前学完小学内容。所有学习任务都遵循「80% 已会内容 + 20% 新内容」的配比，把 \u0026ldquo;赢\u0026rdquo; 的门槛降到最低，让孩子每一次学习都能获得正向反馈\n游戏化浸润，拒绝机械灌输\n把所有知识点融入生活、游戏、肢体互动中，让学习和快乐强绑定，而非和 \u0026ldquo;约束、枯燥\u0026rdquo; 绑定\n容错成长，构建安全型学习心理\n绝对不因为出错否定孩子，先肯定再修正，让孩子不怕做错、敢开口、敢尝试，避免 \u0026ldquo;不自信、怕错题\u0026rdquo; 的问题\n家长误区 误区 1：贪多求快，过度超前，违背发展规律 错误表现：一天让孩子认 10 个以上的字、背多首古诗、学一堆拼音、刷几十道计算题，总觉得学的越多越好，提前学完小学内容。\n危害：超出孩子的记忆容量和注意力极限，导致孩子记不住、持续挫败，最终抵触学习\n误区 2：把学习和坐好不动安静强行绑定 错误表现：完全无视 6 岁男孩的动觉学习特质，禁止动作只会让孩子更分心、更抵触学习，把学习和 \u0026ldquo;束缚、痛苦\u0026rdquo; 绑定。\n建议：给孩子准备学习专属把玩物（光滑鹅卵石、解压捏捏乐、无多余零件的魔方），仅在学习时可以使用，满足孩子手部动作需求\n误区 3：即时纠错、频繁批评，打击孩子的自信 错误表现：孩子一出错就立刻打断，说 \u0026ldquo;你错了\u0026quot;\u0026ldquo;刚教完就忘，你怎么这么笨\u0026rdquo;，甚至打骂，觉得纠错越多孩子学得越快。 误区 4：用物质奖励、惩罚威胁驱动学习 错误表现：用 \u0026ldquo;学完给你买玩具\u0026rdquo; 奖励，用 \u0026ldquo;学不会就不能出去玩\u0026rdquo; 威胁，把学习和物质、惩罚强绑定。\n危害：彻底破坏孩子的内在学习动机，孩子会觉得 \u0026ldquo;学习是为了拿奖励、避惩罚\u0026rdquo;，一旦没有奖励，就完全不想学，同时把学习当成一件痛苦的事，加剧抵触。\n误区 5：教一遍就要求孩子记住，无视记忆规律 错误表现：刚教完的内容，几分钟后就考孩子，忘了就批评 \u0026ldquo;你有没有认真听\u0026rdquo;，觉得教一遍孩子就必须记住。\n危害：6 岁孩子的记忆需要多次重复、多感官输入才能巩固，单次教学就要求完美记住，只会让孩子不断自我否定，觉得 \u0026ldquo;我记不住，我不行\u0026rdquo;，加剧不自信。\n误区 6：横向对比，拿孩子和别人攀比 错误表现：总把 \u0026ldquo;别人家的孩子\u0026rdquo; 挂在嘴边，对比认字量、计算能力，否定孩子的进步。\n危害：彻底摧毁孩子的自信心，让孩子觉得 \u0026ldquo;我永远不如别人\u0026rdquo;，产生强烈的自卑心理，哪怕有进步也没有成就感，最终破罐破摔。\n误区 7：把自身焦虑传递给孩子，情绪不稳定 错误表现：自己的幼小衔接焦虑全部投射到孩子身上，教不会就发脾气、情绪失控，让孩子在紧张恐惧的氛围中学习。\n危害：孩子会敏锐地感受到家长的焦虑，把学习和 \u0026ldquo;爸爸妈妈不开心、我会挨骂\u0026rdquo; 绑定，越学越害怕，越学越抵触，同时破坏亲子关系。\n他的\u0026quot;时间表\u0026rdquo; 时间表 这是我跟他一起做的课表，每天做什么都有他自己安排。\n和孩子一起制定的时间表\n\u0026ldquo;徽章\u0026quot;积分体系 有了课表之后，怎么驱动他去完成呢？\n买了一个\u0026quot;徽章\u0026quot;护照，积满一页就可以兑换一个愿望。\n这里有一个问题？\u0026ldquo;徽章\u0026quot;本质还是积分，是否会将积分与学习强绑定，反而影响孩子的学习兴趣呢？\n错误设计要避免 ❌ 把积分和物质奖励强绑定（只能换玩具、零食、电子产品时长），且学习的唯一正向反馈只有积分 ❌ 积分只奖励「正确的结果」（认对 5 个字得 1 分、做对 10 道题得 1 分、背会古诗得 2 分），做错、没学会就拿不到积分 ❌ 用高价值玩具、大额消费作为核心兑换项，不断加码奖励留住孩子的学习动力；后来没有上百积分对应的贵重礼物，孩子就完全不肯配合。家长只能不断提高奖励标准，一旦停止或降低标准，孩子的学习动力会直接归零 ❌ 用积分和奖励威胁孩子，比如「你不好好学，就别想拿积分换礼物」「学不完今天的内容，积分全部清零」 ❌ 设置严苛的扣分项（走神扣 1 分、做错扣 1 分、没打卡扣 2 分）、随意更改规则、家长主观评判积分是否发放；孩子会觉得这套体系是用来「管他、罚他」的，对打卡和学习产生强烈抵触 正确做法 \u0026ldquo;徽章\u0026quot;是奖励的具象化，不与学习强绑定，一切进步皆可奖励，比如：主动完成作业、不说脏话、主动承担家务。\n奖励的结果是心愿，心愿可以是玩具、大餐、旅行等等，我们也可以在他许愿之前先进行种草。\n徽章护照\n后记 目前我们的\u0026quot;徽章\u0026quot;积分体系，已经运行了 一个多月，孩子也已经适应了。\n给我感触最深的一点是，孩子和父母之间的空间是一定，如果父母管的多，孩子就占的少；父母占的少，孩子自然就做得多。孩子的能量是无限的。 比如，有一次，孩子读完绘本之后，我让他数数总共有多少个字，每行12个字。他竟然说，把这两个盖起来，10个一组，20、40、60 \u0026hellip;\u0026hellip; 这是我们原来没有教过的。\n10个一组数字数\n","permalink":"https://www.chunshuijiancha.top/posts/baby/preschooler/timetable/","summary":"通过时间表和\u0026quot;徽章\u0026quot;积分体系，帮助孩子掌控自己的时间表","title":"掌控自己的时间表"}]