Design: LLM Application Foundation Concepts
Date: 2026-06-23 Status: Draft Scope: LLM app architecture / LLM request builder / context retrieval / inference / tool calling / evaluation / retaintive AI foundation
0. 为什么先写这份
我们之前直接跳进 contacts-analyzer prompt review,是能改出更好的 prompt 的;但如果概念地基没统一,后面会反复卡在同一类问题:
- prompt 里到底该写什么?
- schema、taxonomy、metadata、writer guard 谁说了算?
- 我们现在是“硬把 Neon 数据塞给模型”,以后是不是要 RAG、tool calling、agent 自己取 context?
- prompt cache / prefix cache 会不会影响 LLM request builder 组织方式?
- AI 做出 proposal 后,如果被 writer 拒绝,怎么留账?
- 改 prompt 后怎么证明它变好了?
这份文档先把一条生产 LLM 请求的全链路讲清楚,然后再映射到 retaintive 当前系统。目标不是追术语,而是形成一个正确 mental model:
Context Retrieval 决定模型看到了什么;LLM Request Builder 决定这些东西怎么变成一次完整模型请求;Schema / Tool Contract 决定模型能输出或调用什么形状;Writer / Policy Guard 决定什么真的发生;Evaluation 决定我们怎么证明它做得对。
0.1 这份文档真正服务什么
这份文档不是为了说“我们马上要上 RAG”,也不是为了证明某个 prompt 写法更高级。它服务三件事:
-
给团队建立共同语言
- 大家先知道一个 LLM app 大概由哪些层组成:identity、context retrieval、LLM request builder、schema/tool contract、inference、tool/action、writer、ledger、eval。
- 以后讨论问题时,不要把所有东西都混叫成“prompt 问题”。
-
给我们自己的系统重新定位
- 我们现在不是一个普通 chat bot。
- 我们是在把 gym/customer side 发生的事情,翻译成 contact profile、task、future objective,以及后续可能由 AI agent 执行的 action。
- 所以我们最重要的不是“prompt 看起来聪明”,而是 evidence 正确、context 正确、output schema 正确、writer 安全、decision 可追溯、eval 可回归。
-
为后续改 prompt / metadata / RAG / tool calling 打地基
- 今天我们主要从 Neon 读取结构化业务数据,然后 push 给模型。
- 未来 tenant 变多、业务知识变多、playbook/policy/CRM/POS 接入变多时,可能需要 RAG、tool calling、tenant pack、agent self-check。
- 但这些不是“替代 prompt”的东西,而是 LLM app 每一层逐渐成熟后的自然演进。
0.2 我们到底要做什么
短期不要先问“要不要 RAG”。更准确的问题是:
所以接下来要做的是一套基础设施梳理:
这就是为什么我们现在要先整理 concept,再继续改 prompt。
1. 一条生产 LLM 请求长什么样
通用链路可以画成这样:
1.1 流程里的关键词旁注
这些词不是装饰,它们决定系统能不能 debug、隔离、复现和回滚。
1.2 Inference / Serving Engine 小图
Serving Engine 可以理解为“模型运行时 + 调度器”。它不改变模型本身,而是负责把很多请求高效、安全地跑起来。
几个容易混的词:
对我们当前系统来说,OpenRouter / provider 承担了大部分 serving engine 细节;我们主要要关心的是:
不要把它理解成“所有系统都必须有完整 RAG 和自托管 vLLM”。我们今天的 contacts-analyzer 用的是 Neon 数据库和 OpenRouter,也仍然符合这条链路:
所以我们不是“没有 request builder”。我们已经有了,只是它还不够清晰:static policy、generated taxonomy、schema reminder、runtime context、playbook snippets、schema/tool contract 和 writer constraints 混得太近。
2. 每一层到底负责什么
2.1 Request / Identity Layer
这一层负责“这是谁的请求、属于哪个客户、能访问什么数据、重试会不会重复写”。
常见字段:
它不应该靠 prompt 实现。不能只在 prompt 里写:
真正的隔离必须在代码和数据访问层完成:
- Neon query 带
store_id/tenant_idscope。 - RAG/vector query 带 tenant namespace 或 metadata filter。
- cache key 带 tenant / auth scope。
- tool call 在代码层校验权限。
- writer 根据 authenticated context 决定能不能写。
2.2 Context Retrieval Layer
这一层负责“模型应该看到哪些事实”。
它可以是 RAG,也可以是数据库查询。不要把 RAG 神化成一定要 vector DB。RAG 的本质是:
数据源可以是:
- Neon tables:
contacts/calls/messages/leads/tasks - Vector DB: docs / policies / CRM notes
- Object storage: long transcript / full prompt / original document
- Tool prefetch: CRM lookup / POS membership status
- Conversation memory: summarized state
RAG 常见流程:
我们当前更像 DB retrieval:
未来是否要 RAG,取决于数据形态:
2.2.1 RAG 和 Neon direct retrieval 的区别
RAG 和 Neon direct retrieval 不是谁一定更高级,而是服务不同数据形态。
Neon direct retrieval 是按明确业务 key 取结构化事实:
它适合:
- 当前客户是谁。
- 当前有哪些 open tasks。
- 最近几通电话说了什么。
- 这个 task 的
taskId/sourceRef是什么。 - 是否 DNC。
- 哪个 store / tenant。
这些事实有明确主键、外键、时间、权限边界。它们应该用 SQL / domain reader / deterministic query 取,不应该先扔进 vector DB 再语义搜索。
RAG 是按语义找“相关知识片段”:
它适合:
- studio playbook。
- policy 文档。
- pricing / promo 说明。
- sales script。
- FAQ。
- long-form historical notes。
- tenant-specific docs。
这些内容不一定有明确 key,用户或模型可能会用不同说法问同一个东西,所以语义检索有价值。
核心区别:
所以未来最可能不是二选一,而是混合:
对我们来说,第一步不是“上 RAG”,而是把 context source 分类好。哪些是业务当前状态,继续走 Neon;哪些是知识材料,才考虑 RAG;哪些是 live authoritative fact,应该走 tool/API 或同步表。
2.3 LLM Request Builder Layer
Prompt Builder 这个名字有点误导。生产系统里它通常不是只 build 一段 prompt string,而是 LLM Request Builder / Context Assembler:
也就是说,它最后产出的不只是 systemPrompt,而是一次 LLM call 的完整输入包:
其中:
messages是模型真正读到的文字输入。responseSchema/toolSchemas通常走 request body,不应该手写成一大段 prompt。modelConfig决定 model、provider、temperature、max tokens、routing。promptMetadata记录 prompt version、schema version、tenant/store、hash、experiment cohort。debugSnapshot给 review / replay / OpenRouter log 对照,不一定直接给模型。
所以这个函数的生产版更像:
它的职责是:
- 选择 prompt version。
- 组装 stable system instructions。
- 注入 common taxonomy / metadata 的精简渲染。
- 注入 tenant config,但不让 tenant config 覆盖 global safety。
- 渲染 runtime context。
- 绑定 output schema / tool schema。
- 绑定 model/provider/generation parameters。
- 记录 prompt hash、schema version、tenant config version、experiment cohort。
- 保持 prompt cache 友好的顺序。
- 输出可测试、可 snapshot 的 final input。
它不应该:
- 让客户自由输入 system instructions。
- 手写第二份 schema。
- 把 writer guard 当 prompt 文案实现。
- 让不同调用路径各自拼一套 prompt。
2.3.1 2026 general best-practice 图
这不是某个供应商逐字规定的唯一标准,而是把 OpenAI / LangSmith / agent tooling 的共同原则收敛成一个工程图:
这里有几个 2026 年比较稳定的原则:
-
Prompt 放 application code 管理
- production prompt 应该像代码一样走 typed inputs、code review、tests、eval、deployment、rollback。
- 不要依赖供应商里的 reusable prompt object 作为唯一 source of truth。
-
Instructions 和 data 分开
- stable instructions / policy / tool definitions 放前面。
- user input / RAG docs / runtime facts 当作 untrusted data 放后面并清楚标边界。
-
Stable prefix 放前面
- prompt/prefix cache 常常要求 exact prefix match。
- 所以 stable content 放前面,dynamic content 放后面。
- 但 cache 不能突破 tenant/auth boundary。
-
Schema 和 tools 是 contract,不是散文
- JSON shape、tool args、enum constraints 应该用 schema/tool definitions 表达。
- prompt 只保留必要提醒,不手写第二份完整 schema。
-
Version 整个 behavior bundle
- 不只 version prompt text。
- 还要记录 model、schema、tool schema、taxonomy/metadata、retrieval config、tenant config、generation params、guardrail/writer version。
-
Eval 是发布流程的一部分
- prompt 改动不是“看起来更好”就完了。
- 每次改 prompt/schema/tool/RAG/writer,都应该跑 regression eval 或至少关键场景。
2.3.2 分层来源不等于同一个 message
下面这个分层是“内容来源和覆盖顺序”,不是说它们都必须塞进同一个 system text:
更准确地说:
2.3.3 我们这个项目应该怎么画
对于 contacts-analyzer,现在更准确的图应该是:
它最后交给 AI helper 的东西应该像:
注意这里的边界:
buildSystemPrompt()只放稳定判断规则和 generated stable sections。buildUserMessage()只放本次 contact 的事实材料。ContactsAnalysisSchema通过 request body 传给 model SDK / validator,不应该再完整手写进 system prompt。taskId/sourceRefs的真实解析在 code,不在 prompt。Policy Guard在 writer 后面,决定 proposal 是否真的写入。
对于 contacts-analyzer,理想 system prompt 内容顺序应该接近:
user message 则放动态内容:
2.4 Output Schema Layer
这一层负责“模型返回的 JSON 长什么样”。
它应该由 Zod / JSON Schema / tool schema 管,不应该靠 prompt 手写。
例子:
生产 prompt 可以提醒:
但不应该再手写一大段:
这些应该来自 schema 或 common typed metadata,并且只在 review/debug artifact 里完整展开。
2.5 Inference Layer
这一层负责“模型怎么跑得快、稳、便宜”。
常见概念:
这层重要,但不是我们当前最先要改的地方。我们现在更应该先把 LLM request builder / schema / writer / eval 分清楚。性能优化应该等行为正确、trace 完整后再做。
2.6 Tool / Action Layer
LLM 不应该直接“做事”。它应该输出 proposal 或 tool call,代码再验证。
对于 retaintive 当前 task 系统:
未来如果有 agent 自己发短信 / 打电话 / 查 CRM,也要保持同一个原则:
2.7 Control / Writer Layer
这一层是真正的权威。
它负责:
- DNC。
- store / tenant isolation。
- duplicate task suppression。
- taskId 是否真的存在且 open。
- sourceRefs 是否能解析。
- idempotency。
- DB transaction。
- stale proposal。
- approval gate。
prompt 可以帮助模型少犯错,但不能替代这里。
这也是 system-worldview 的核心:
2.8 Ledger / Observability Layer
这一层回答:
只写 CloudWatch log 不够。业务关键 AI decision 应该有 durable ledger。
从我们读到的 AI 决策可追溯性文档看,推荐方向是:
也就是:
contacts/tasks/calls继续保存当前状态。- accepted task evidence 继续用
task_sources。 - task progress 继续用
task_progress_events。 - AI suggestions/playbooks 继续 append-only。
- 但 rejected / skipped / zero-row proposal 需要补
task_decision_attempts这类账本。 - 每次 LLM run 需要
ai_runs或等价 durable trace,记录 prompt/model/token/cost/hash。
2.9 Evaluation Layer
这一层回答:
不要只看“感觉回答不错”。生产 LLM app 至少需要:
- Programmatic checks: JSON schema、tool args、权限、DB result。
- Offline golden eval: 固定场景集,比较 expected decision。
- DB replay: 验证 writer / Policy Guard / DB constraints。
- LLM judge: 评估开放式 summary / groundedness。
- Human review: 校准 judge,处理高风险 case。
- Shadow / Canary / A/B: 上线前后比较真实流量。
- Production feedback: staff close result、correction、thumbs up/down、business outcome。
对我们来说,先要补的是 Task Accuracy Foundation:
3. Prompt / Prefix Cache 应该怎么理解
Prompt cache 的核心不是“把 prompt 存起来”,而是复用相同前缀的计算结果。
如果每次请求都是:
那前面稳定的 5,000 tokens 理论上不需要每次都重新 prefill。
很多实现要求 exact prefix match。OpenAI 文档也明确建议:
所以 Prompt / Request Builder 的顺序很重要:
但 multi-tenant 系统不能为了 cache 随便共享:
对于我们当前阶段:
- 先把 stable system prompt 和 dynamic user message 分清楚。
- 不要把 contact-specific facts 放进 system prompt。
- 不要让 tenant-specific 或 user-specific 内容打碎最前面的 shared prefix。
- 不要为了 cache 牺牲 tenant isolation。
4. Prompt Registry / Versioning 应该怎么理解
Prompt versioning 不是只给一段 prompt 文本起名。
一次 LLM 行为由这些共同决定:
所以真正要记录的是 behavior bundle。
推荐记录:
Prompt Registry 可以是:
- Git + code review + tests。
- internal DB/config service。
- LangSmith/Langfuse/Braintrust 这类平台。
OpenAI 2026 的变化值得注意:它正在把 reusable prompt objects 从 API 里迁出,推荐迁回 application code。但这不代表 Prompt Registry 这个工程概念过时。更准确的理解是:
5. Prompt Injection 和数据边界
所有这些都要视为不可信数据:
- user input。
- RAG document。
- web page。
- email。
- Slack message。
- transcript。
- CRM note。
它们里面可能包含:
防护原则:
- 指令和数据在 prompt 中明确分区。
- 权限在代码层判断,不让模型自己决定。
- tools 使用 allowlist 和严格 schema。
- 高风险 tool 需要 human approval。
- tool output / external docs 不能升级成 system instruction。
- model output 必须 validation 后才能执行。
- 对 rejected / blocked action 留痕。
这和我们的 system-worldview 一致:AI 不能拥有最终写入权,只能在代码授权范围内行动。
6. RAG、DB Retrieval、Tool Calling 的关系
不要把它们当成互相替代的 buzzword。它们解决的是同一个问题的不同部分:模型如何拿到当前任务所需的外部事实。
我们今天的 contacts-analyzer 是 push context:
未来可能演进成 pull context:
但演进顺序应该是:
原因很简单:如果现在连“模型为什么建/没建 task”都查不清,直接上 tool-calling agent 会把不可解释性放大。
7. 映射到 retaintive:我们现在有什么
当前已经有一部分 AI foundation:
当前缺口也很明确:
- LLM request builder 分层还不够清楚。
- output contract reminder / injected contract 和 Zod schema 有重复。
- taxonomy / metadata / field applicability 还没有统一 typed source of truth。
- task playbook guidance 现在主要按 open task category 注入,create-side guidance 不完整。
- rejected / skipped task proposals 还缺 durable decision ledger。
- prompt/model/schema/retrieval/config 的 behavior bundle 还没有完整 durable run record。
- golden eval / DB replay 需要扩容。
8. 在继续改 prompt 前,应该先收集什么
先不要继续凭感觉改 prompt。先做 inventory。
8.1 AI output surfaces
列出所有 AI 会产出的东西:
每个 output surface 都要回答:
8.2 Context sources
列出模型可能看到的所有事实源:
每个 source 都要标:
8.3 LLM request components
把每个 LLM call 拆成:
然后判断:
8.4 Versioning bundle
每次 LLM call 至少应该能记录:
8.5 Eval and failure cases
收集:
每个 case 写 expected behavior,不要只写“模型应该更聪明”。
9. 建议学习顺序
Step 1: 先学“一条请求怎么流”
目标:能画出从 trigger 到 DB write 的全链路。
先读:
- system-worldview。
- AI Agent Foundation final state。
- 本文档第 1-2 节。
你要能回答:
Step 2: 学 data/context,不要先学 prompt tricks
目标:知道 context 是怎么来的,以及哪些证据可信。
重点:
- Neon reader 怎么选 contact/calls/messages/tasks。
- sourceRefs / taskRefs 怎么渲染和解析。
- fresh evidence vs stale snapshot。
- DB retrieval 和 RAG 的区别。
- tenant/store isolation。
Step 3: 学 LLM Request Builder
目标:把一次 LLM call 看成 typed request,不是长字符串。
重点:
- stable system prompt vs dynamic user message。
- common taxonomy / metadata 生成 section。
- tenant config 怎么注入。
- output schema / tool schema 怎么通过 request body 提供。
- model/provider/generation params 怎么绑定到这次 call。
- prompt hash / schema version / context builder version 怎么记录。
- prompt cache 为什么要求稳定前缀。
Step 4: 学 Writer / Policy Guard
目标:理解“模型说了不算,writer 才算”。
重点:
taskDecisions[]怎么变成 commonTaskAction。- DNC / duplicate / task_not_open / store mismatch 怎么拦。
- idempotency 和 zero-row race。
- rejected proposal 为什么要 ledger。
Step 5: 学 Eval
目标:以后改 prompt 不靠感觉。
重点:
- golden scenarios。
- programmatic checks。
- DB replay。
- LLM judge 的位置和局限。
- shadow / canary / A/B。
- production failure 怎么回灌 regression set。
Step 6: 再学 inference / caching / performance
目标:知道成本和延迟怎么受 prompt 和 serving 影响。
重点:
- token / prefill / decode。
- TTFT / ITL / E2E latency。
- prompt cache / prefix cache。
- KV cache。
- continuous batching。
- chunked prefill。
这一步现在不用深挖到 GPU kernel。先知道为什么 LLM request builder 要把 stable prefix 放前面,为什么 dynamic context 放后面。
Step 7: 最后再学 agent / tool calling
目标:知道什么时候可以让 AI 自己取 context 或执行工具。
前提:
- typed tool schema。
- capability envelope。
- approval queue。
- execution log。
- durable decision ledger。
- eval baseline。
没有这些,不要直接上“agent 自己决定查什么、做什么”。
10. 建议执行路线
Phase 0: 暂停大规模 prompt 文案改动
不要继续凭直觉改 contacts prompt。先把地基文件补齐。
Phase 1: AI call inventory
产物:
内容:
Phase 2: Context source inventory
产物:
内容:
Phase 3: LLM Request Builder architecture
产物:
目标:
Phase 4: Decision ledger / AI run ledger
产物:
目标:
Phase 5: Eval baseline
产物:
目标:
Phase 6: 再回到 prompt 改写
这时改 prompt 才是正确顺序:
11. 读资料顺序
必读内部文档
- system-worldview.md
- ai-agent-foundation-final-state.md
- prompt-architecture-and-agent-evolution.md
- 3-prompt-architecture.md
- 飞书文档:AI 决策可追溯性 / decision ledger 两份文档。
必读官方/外部资料
- OpenAI Prompt Caching: https://developers.openai.com/api/docs/guides/prompt-caching
- OpenAI Latency Optimization: https://developers.openai.com/api/docs/guides/latency-optimization
- OpenAI Evaluation Best Practices: https://developers.openai.com/api/docs/guides/evaluation-best-practices
- OpenAI Prompt Object Migration: https://developers.openai.com/api/docs/guides/prompting/migrate-from-prompt-object
- vLLM Prefix Caching: https://docs.vllm.ai/en/stable/design/prefix_caching/
- Pinecone Multitenancy: https://docs.pinecone.io/guides/index-data/implement-multitenancy
- NVIDIA Triton Batcher: https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/batcher.html
- OWASP LLM Prompt Injection Prevention: https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html
- LlamaIndex RAG intro: https://developers.llamaindex.ai/python/framework/understanding/rag/
- LangSmith Prompt Engineering Concepts: https://docs.langchain.com/langsmith/prompt-engineering-concepts
12. 当前调研依据
Verified 2026-06-23 with local rg / sed, Lark docs fetch, and official web docs.
Local commands used:
Important current-code anchors:
lambda/contacts-analyzer/src/core/prompt-builder.tsownsbuildSystemPromptResult()andbuildUserMessage().lambda/contacts-analyzer/src/core/models.tsownsContactsAnalysisSchemaandTaskDecision.lambda/shared/utils/ai/invoke.tssendsgenerateObject({ schema: zodSchema, mode: 'json', system, prompt }).lambda/contacts-analyzer/src/infrastructure/neon-repository.tsmapstaskDecisions[]into commonTaskActionwrites.@retaintive/common/domainownsTaskAction,applyTaskAction(), and Policy Guard direction.@retaintive/common/taxonomy/contactand@retaintive/common/taxonomy/taskown current shared taxonomy definitions.
Important external anchors:
- OpenAI prompt caching requires exact prefix matches and recommends stable content first, dynamic content later.
- OpenAI latency guidance emphasizes fewer output tokens, fewer input tokens, fewer requests, parallelization, and not defaulting to LLMs.
- OpenAI is moving reusable prompt objects out of API-managed objects and into application code.
- vLLM prefix caching reuses KV cache blocks for requests with the same prefix.
- Pinecone recommends one namespace per tenant for multitenant vector isolation.
- OWASP treats prompt injection as an application security problem requiring defense in depth, not prompt text alone.