LobeHub 应用级 LLM 生成规范Prompt 归属、Scenario 语义与 llm_generation_tracing 追踪实践【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub在 LobeHub 这类需要大量调用 LLM 生成业务内容任务意图识别、简报生成、记忆抽取、验证规划等的 Agent 运营平台中每一次generateObject/generateText调用如果缺乏统一的 prompt 归属、模型策略与追踪规范很快就会导致评估数据与线上追踪数据互相污染。本篇基于仓库中的 LLM Generation 技能文档 展开结合 AiGenerationService、TRACING_SCENARIOS 目录 与 llm-generation-tracing 包 的源码系统讲解如何把业务 LLM 调用实现为“显式、可独立观测”的工作流读完你能掌握 LobeHub 中 prompt 链的封装方式、scenario/promptVersion/schemaName三件套的语义边界以及一套完整的新增生成工作流验证清单。一、先定位既有边界动手改调用之前要检查的四处代码技能文档的第一条原则是“在编辑任何调用之前先检查既有边界”。在 LobeHub 中一次业务 LLM 调用的职责被明确拆分为四个层次修改调用前必须逐层确认检查点仓库路径职责可复用的应用级 promptpackages/prompts消息构造器、JSON schema、schema 名称、prompt 版本的统一导出服务端结构化生成封装apps/server/src/services/aiGenerationAiGenerationService统一运行时初始化与generateObject调用Scenario 名称目录packages/const/src/llmGenerationTracing.tsTRACING_SCENARIOS常量映射所有追踪场景名的唯一权威来源追踪选项与注册表行为packages/llm-generation-tracingTracingOptions类型、resolveScenario注册表、本地文件存储归属服务各业务 service 目录模型配置、Zod 校验、持久化、业务错误处理文档同时划清了一个容易混淆的边界agent-tracing用于执行快照execution-snapshot诊断agent-runtime-hooks用于生命周期钩子行为二者都不负责应用级 LLM 生成规范。换句话说llm_generation_tracing记录的是“应用自己发起的一次结构化生成”而不是 Agent 运行时的执行快照——两者不可互相替代。服务端统一封装AiGenerationService从源码看几乎所有服务端产生结构化输出的调用都经过同一个两步流程先从数据库解析用户的 provider 配置再调用generateObject。AiGenerationService 的存在就是为了消除每个调用点重复这套初始化接线export class AiGenerationService { // 每请求构造一个实例 —— db 与 userId 来自请求上下文 constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { /* ... */ } async generateObjectT unknown( input: AiGenerationObjectInput, options: AiGenerationObjectOptions {}, ): PromiseT { const runtime this.workspaceId ? await initModelRuntimeFromDB(this.db, this.userId, input.provider, this.workspaceId) : await initModelRuntimeFromDB(this.db, this.userId, input.provider); return (await runtime.generateObject( { messages: input.messages, model: input.model, schema: input.schema, /* ... */ }, { metadata: options.metadata, signal: options.signal, tracing: options.tracing }, )) as T; } }源码注释明确指出该服务的设计意图“几乎每个服务端调用产生结构化输出都要走同样的两步从 DB 解析用户 provider 配置然后带着调用方特定 metadata 调用 generateObject。这个服务存在是为了让调用点不必重复 init 接线也让未来横切关注点默认 metadata、重试、可观测性默认值有一个统一的落点”。这与技能文档中“优先使用共享的服务端生成服务使运行时初始化、路由与追踪保持一致”的 Model Policy 条款一一对应。注意options.tracing字段在调用点由lobechat/llm-generation-tracing的TracingOptions强类型约束而options.metadata则留给计费、路由等非追踪钩子——两个通道各司其职。二、Prompt 归属与版本化*_PROMPT_VERSION与提示词同模块导出这是整份规范中最核心的架构约束可归纳为三条铁律可复用的生成契约放入packages/prompts/src/chains消息构造器、JSON schema、schema 名称、prompt 版本必须一起导出不得把成体系的 system prompt 或面向模型的输入序列化逻辑内嵌在 service 里。执行相关关注点留在归属服务端模型配置、AiGenerationService、追踪实体 ID、Zod 校验、持久化与业务错误处理都不属于 prompt 链。版本常量与它描述的 prompt 放在一起并从同一模块导出。真实示例taskIntent 链packages/prompts/src/chains/taskIntent.ts 是上述契约的一个标准实现它把“版本常量 JSON schema 消息构造器”组织在同一模块内// 当“创建任务意图识别” prompt 发生实质变化时提升版本 export const TASK_INTENT_PROMPT_VERSION v1; export const TASK_INTENT_KINDS [task, goal] as const; export const TASK_INTENT_CONFIDENCES [high, medium, low] as const; export const TASK_INTENT_JSON_SCHEMA { name: task_intent, // 稳定的 workflow 级 schema 名称 schema: { /* additionalProperties: false 的严格结构 */ }, strict: true, }; export const chainTaskIntent ({ context, instruction }: TaskIntentInput) ({ messages: [ { role: system, content: [ /* 行为约束提示词 */ ].join(\n) }, { role: user, content: ## Request\n${instruction} }, ], });文档中给出的最小模式如下新增链时应照此组织export const EXAMPLE_PROMPT_VERSION v1; export const EXAMPLE_SYSTEM_PROMPT ...;版本号的格式与提升时机格式vmajor或vmajor.minor例如v1或v1.2promptVersion中只存版本号不要包含功能或场景前缀如expertise-ingestion-v1——workflow 身份由scenario字段承载提升时机只要 prompt 或输出契约的变化“应该形成一个独立的评估或追踪队列”就提升版本。registry.ts 的源码注释解释了为什么版本必须“住在 prompt 旁边”版本刻意放在它描述的 prompt 旁边见 generateObject 调用点附近的*_PROMPT_VERSION常量。当 prompt 或 schema 变化时提升那个本地常量——让版本和它描述的东西放在一起避免了“没人记得去更新的中心表”造成的漂移。三、Scenario 语义产品工作流的稳定分区键不是提示词标签文档对scenario的定性是它是稳定的产品 workflow 与生命周期阶段分区而不是 prompt、schema、模型或辅助函数的标签。操作规则有四条新增调用前先查TRACING_SCENARIOS仅当用户可见工作流与生命周期阶段完全相同时才复用某个 scenario业务动作不同就必须新增 scenario——即使另一次调用共享了它的 prompt 或 JSON schema。文档给出的例子很典型可编辑的目标标准草稿goal-criteria drafting与运行时的验证规划run-time verification planning是两个不同 scenario绝不“就近借用”一个相邻 scenario 当占位符——这会污染延迟、成本、成功率与质量数据。此外结构化生成要传schemaName有可用实体 ID 时也要一并传递。源码印证场景目录与解析顺序packages/const/src/llmGenerationTracing.ts 维护了全部llm_generation_tracing场景值的权威目录注释明确写着“值会被持久化到行上的scenario列是 dashboard/分区键必须保持稳定”export const TRACING_SCENARIOS { AgentSignal: agent_signal, AgentWelcome: agent_welcome, BuilderSuggestion: builder_suggestion, GoalCriteriaGen: goal_criteria_gen, GoalDecompose: goal_decompose, HomeBrief: home_brief, InputCompletion: input_completion, MemoryExtract: memory_extract, TaskBrief: task_brief, TaskBriefJudge: task_brief_judge, TaskIntent: task_intent, TopicTitle: topic_title, TopicAutoSummary: topic_auto_summary, VerifyJudge: verify_judge, VerifyPlanGen: verify_plan_gen, VerifyReport: verify_report, Unknown: unknown, // ... 共 30 个场景 } as const;注意VerifyPlanGen与GoalCriteriaGen同时存在正是文档中“即使共享 prompt/schema 也要按业务动作拆分 scenario”原则的实例。resolveScenario 定义了 scenario 的三级解析顺序export const resolveScenario (input: ResolveScenarioInput): ScenarioDefinition { const scenario input.scenario ?? // 1. 显式传入优先 (input.trigger ? TRACING_SCENARIO_REGISTRY[input.trigger] : undefined) // 2. trigger 注册表 ?? UNKNOWN_SCENARIO; // 3. unknown 哨兵 return { promptVersion: input.promptVersion ?? UNKNOWN_PROMPT_VERSION, // 注册表从不分配版本 scenario, }; };TRACING_SCENARIO_REGISTRY 目前只登记了agent_signal、memory、signup_email_llm_review、topic四个“trigger → scenario”的默认映射注释特别说明像agent_signal这种会扇出到多个子场景signal_skill_intent/signal_feedback_satisfaction等的 trigger故意不设默认项由调用方显式传metadata.scenario——这正是“不借用占位 scenario”规则在机制层面的保障。TracingOptions调用方的追踪配置面TracingOptions 是每次generateObject调用的调用方配置所有字段可选钩子会填默认值自动提取inputHint、注册表解析scenario、messages[0]作为 system prompt但“显式提供字段才能让 DB 行保持可扫描”。关键字段语义字段说明agentId/topicId归属 agent 与 topic/conversation 实体 IDinputHint存入input_hint列的短片段当 prompt 用模板包裹用户文本时必须显式传入否则自动提取到的会是模板外壳的第一条消息metadata自由上下文写入行上metadatajsonb 列如关联 IDparentTracingId链式生成的父追踪行promptVersion语义化 prompt 版本如v1.0scenario场景名缺省时回退到按trigger查注册表schemaName结构化输出的 schema 标识systemPrompt覆盖 prompt-hash 的 system 文本默认取messages[0]若为 system 消息tracingId调用方提供的 UUID 主键——当路由需要在生成完成前就知道 ID例如提前返回给客户端以便后续提交反馈时传入triggerRequestTrigger字符串四、模型策略显式解析不静默继承文档的 Model Policy 共四条逐条拆解通过归属服务的配置策略解析模型与 provider不要静默继承无关的聊天模型——这直接对应 AiGenerationService 中input.provider显式传入initModelRuntimeFromDB的做法provider 是调用的必填输入而不是从某个全局“当前聊天模型”里偷偷拿当某 workflow 需要稳定的服务模型时给它显式默认值并暴露对应的服务模型配置而不是只在调用点硬编码模型名模型选择与 prompt 版本解耦——更换配置的模型不改变 prompt 或 scenario 名称追踪数据里model与prompt_version是两个独立列正是这种解耦的体现见下文TracingSummary调用适配时优先走共享服务端生成服务保证运行时初始化、路由与追踪一致即第一节的AiGenerationService。五、结构化生成schema 命名、提示词-结构对齐与边界校验文档给出四条规则并结合源码可以看得更具体每个 JSON schema 要有稳定的、贴合 workflow 的名称。以 taskIntent 链 为例schema 名task_intent与TRACING_SCENARIOS.TaskIntent task_intent同源namestrict: trueadditionalProperties: false三件套让输出结构完全封闭提示词指令与 schema 要求必须对齐schema 中的必填字段必须由提示词提供。taskIntent的 system prompt 逐一解释了title/summary/refinedInstruction/kind/confidence/clarifications的生成规则例如“confidence 为 high 必须伴随空的 clarifications 列表”与 schema 的required数组一一对应在 service 边界校验生成内容并保留归属服务的 fallback/错误行为——lobechat/llm-generation-tracing的 TracingPayload 中专有validation_failed?: boolean与error?: TracingErrorPayload字段说明“校验失败”本身也是一类被追踪的一等事件不要用 schema 名相同来为复用无关的追踪 scenario 辩护——schema 身份与 scenario 身份是两个维度。追踪载荷与本地存储布局TracingPayload完整镜像了设计文档的 Blob schemaDB 行存可索引的摘要列Blob 携带完整的 prompt/输入/输出细节供离线分析version: 1.0字段为未来 schema 演进做防护。而 TracingSummaryscenario、prompt_version、model、latency_ms、success、validation_failed正对应文档中 scenario 语义一节所警告的“延迟、成本、成功率、质量数据”四组被分区统计的指标。开发/本地环境使用 FileTracingStore按如下布局写入纯 JSON可用cat直接检查.llm-generation-tracing/{scenario}/{promptVersion}-{promptHash}/{file}.json顶层保留一个latest.json软链接指向最新记录list支持按scenario过滤并跳过损坏文件。可以看到scenario与promptVersion同时作为存储分区的两层目录名——这从存储层面印证了“scenario 是 workflow 分区键、promptVersion 是评估队列键”的双维度设计。六、验证清单新增或修复生成工作流后的五步检查文档的 Verification 章节是一份可直接执行的验收清单五个步骤与仓库中的验证手段对应如下断言实际发出的scenario、promptVersion与schemaName如适用。由于 resolveScenario 存在“显式 scenario → trigger 注册表 →unknown”的兜底链测试断言能有效防止调用点悄悄落到unknown哨兵测试 prompt 的关键行为约束而不是对整个散文做快照——例如对taskIntent应断言“高置信度必须伴随空 clarifications”这类行为契约而非逐字锁定整段 system promptchains 目录下的测试文件如builderSuggestion.test.ts、taskIntent.test.ts、verify.test.ts即为这一模式的存量实践测试结构化输出校验与相关失败行为——包括validation_failed路径全局搜索过期的内嵌 prompt、旧版本字符串、被错误复用的 scenario——因为版本常量与 prompt 同模块grep旧版本号字符串如v1升到v2后残留的v1是低成本的回归检查跨包变更时运行bun run check changed-files...与bun run check --type——packages/prompts、packages/const、packages/llm-generation-tracing分属不同包类型检查必须跨包执行。文档最后还指出测试机制细节参见testing技能TypeScript 变更规范参见typescript技能——本技能只约定“生成工作流”这一主题域内的规则与其他技能文件职责不重叠。七、规范要点速查概念规则源码依据Prompt 契约消息构造器 JSON schema schema 名 版本同模块导出放packages/prompts/src/chainstaskIntent.ts版本号仅vmajor[.minor]无前缀与 prompt 同模块契约变化即提升registry.ts 注释Scenario稳定的产品 workflow 分区键先查TRACING_SCENARIOS禁止借用占位llmGenerationTracing.ts解析兜底显式 scenario → trigger 注册表 →unknown缺失版本记v0resolveScenario模型策略显式 provider/model 解析禁止静默继承聊天模型模型与 prompt 版本解耦AiGenerationService结构化输出稳定 schemaName提示词与 required 字段对齐service 边界校验并保留 fallbacktaskIntent schema、TracingPayload存储分区.llm-generation-tracing/{scenario}/{promptVersion}-{promptHash}/file-store.ts八、小结LobeHub 的 LLM Generation 技能 本质上是一份“应用级 LLM 调用的工程宪法”它用packages/prompts管住 prompt 的身份与版本用TRACING_SCENARIOSresolveScenario管住工作流的观测分区用AiGenerationService管住模型解析与运行时一致性再用五步验证清单把三者锁死。对贡献者而言判断一份新调用是否合规的最快路径就是核对三件事——prompt 版本是否和提示词同模块、scenario 是否来自 TRACING_SCENARIOS 且语义上确属同一用户可见 workflow、以及测试是否断言了实际发出的scenario/promptVersion/schemaName三件套。这三条同时满足一次生成调用就具备了被独立评估、独立追踪、独立演化的资格。【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考