AI Chat的上下文工程:别再把聊天框当 textarea
做 AI Chat 产品时第一版往往长得很像一个输入框、一个消息列表、一个模型下拉框。这个形态容易让团队误判以为核心工作只是“把用户输入发给模型再把回复流式渲染出来”。真正进入可用阶段后问题会马上变复杂用户上传了文件模型该看到全文还是摘要用户昨天聊过的项目今天要不要自动带入工具执行失败后下轮对话该不该知道失败原因同一段上下文既要给模型用又要给前端展示还要能计费、审计、导出。这就是 AI Chat 上下文工程。聊天框只是入口产品真正要做的是把消息、文件、工具结果、记忆和预算约束组织成一份“模型可以执行、用户可以理解、系统可以追踪”的上下文包。1. 消息上下文历史消息不是越多越好最常见的错误是把最近 N 条消息直接塞回请求。它短期能跑但会带来三个问题噪声累积用户一次闲聊、一次纠错、一次跑题都会被下一轮继承。意图漂移模型会把旧任务当成当前任务的一部分。成本失控上下文窗口越大延迟、费用和失败概率都会上升。更稳的做法是把消息分成四类类型是否进入模型上下文保存目的例子当前意图必进驱动本轮回答“把这份 PDF 总结成提纲”任务约束条件进入保持一致性“输出中文不要表格”历史结论摘要进入延续项目状态“上次选了方案 B”闲聊/废话默认不进只做 UI 展示“好的谢谢”这件事听起来像后端策略其实前端也必须参与。消息组件如果只知道 role/content很难表达“这条消息是展示记录还是可复用上下文”。更合理的数据结构至少要有context_policytypeChatMessage{id:string;role:user|assistant|tool;content:string;createdAt:string;contextPolicy:always|summary|on_demand|display_only;source?:typed|file|tool|memory;};这不是过度设计。只要产品支持文件、工具、项目、长对话其中一个字段的缺失都会在后面变成“为什么模型老是记错”的用户抱怨。2. 文件上下文上传成功不等于上下文成功用户上传文件后很多系统会走向两个极端要么直接把全文塞给模型要么只存一个文件链接让模型“自己理解”。前者成本高后者模型根本看不到内容。文件上下文要分三层原始资产文件本体、大小、类型、权限、过期策略。可检索表示分段、摘要、标题、页码、embedding 或关键词索引。本轮引用这次任务真正需要的片段以及为什么选中这些片段。对于 AI 工作区第三层最关键。用户问“这份合同有什么风险”时模型需要的是条款片段和引用位置用户问“把论文改成答辩 PPT”时模型需要的是章节结构、核心结论和图表说明。两个任务用同一个文件但上下文包完全不同。一个实用原则上传后的第一步不是“问模型”而是“建立文件目录卡”。例如{asset_id:file_123,kind:pdf,derived_context:{outline:[摘要,方法,实验,结论],key_entities:[多模型路由,成本控制,失败重试],retrieval_units:42},permission:owner_only}这张目录卡不替代原文但能让系统在每轮对话前先判断要不要检索、检索哪一段、返回给模型多少内容。3. 工具上下文工具结果不是聊天记录AI Chat 产品一旦接入联网搜索、代码执行、图片生成、文档导出就会遇到工具上下文问题。很多实现会把工具调用结果直接追加成一条消息这会让聊天记录越来越像日志文件。更好的边界是工具结果先进入“任务状态”再按需要投影到消息流。typeToolResult{runId:string;tool:web_search|file_parse|ppt_export|image_generate;status:success|failed|partial;userVisibleSummary:string;modelContext:string;artifacts?:Array{name:string;url:string;mime:string};};这里有一个容易被忽略的点userVisibleSummary和modelContext不应该总是相同。用户需要看的是“发生了什么、产物在哪”模型下一轮需要的是“哪些事实已经确定、哪些约束必须继承”。如果这两者混在一起UI 会变啰嗦模型也会被无关日志污染。4. 记忆与工作区跨会话状态要有刹车很多团队一听到 memory就想把所有用户偏好都存起来。这个方向危险。记忆的价值不在数量而在可撤销、可解释、可作用域化。我更建议把记忆拆成三类用户偏好语言、格式、常用风格。项目事实某个项目的技术栈、目标、命名约定。临时任务状态本次任务已经完成什么、下一步是什么。前两类可以跨会话第三类应该有明确生命周期。否则用户一次临时要求“这次用英文”会变成系统永久偏好一次测试项目名会污染所有后续对话。AI 工作区的关键不是“记住一切”而是让用户知道系统记住了什么、为什么使用、如何删除。5. 普通 Chat UI 和 AI 工作区的差异维度普通 Chat UIAI 工作区输入单条文本文本 文件 项目 工具选择历史最近消息可筛选、可摘要、可作用域化的上下文文件附件展示文件目录卡 检索片段 引用工具调用日志任务状态 可复用产物记忆隐式保存可解释、可撤销、按项目隔离成本请求级统计上下文预算 模型选择策略这个表的核心结论是AI Chat 的前端架构不能只围绕 message list 设计而要围绕 context assembly 设计。6. 一个最小可落地的上下文包如果你正在做一个 AI Chat 产品可以先从这个结构开始typeContextPacket{intent:{rawInput:string;normalizedTask:string;};messages:Array{id:string;role:string;content:string;reason:current|constraint|summary;};assets:Array{id:string;selectedChunks:string[];citationRequired:boolean;};toolState:Array{runId:string;status:string;modelContext:string;};memory:Array{scope:user|project;text:string;confidence:number;};budget:{maxInputTokens:number;preferredModel:string;};};注意这里没有把所有内容揉成一个 prompt。结构化上下文的好处是每一部分都能被观察、测试和降级文件检索失败时可以只回答已知部分记忆冲突时可以要求确认预算不足时可以先压缩历史摘要。7. 常见问题Q: 小团队需要一开始就做这么完整吗A: 不需要。第一版只要把消息、文件、工具结果分开存就已经避开了最大坑。等到长对话和多文件场景出现再补检索和记忆。Q: 上下文工程和 Prompt Engineering 有什么区别A: Prompt Engineering 偏“怎么写指令”上下文工程偏“给模型什么材料、以什么结构给、失败时怎么降级”。后者更接近产品架构。Q: 为什么这对前端开发者也重要A: 因为上下文的很多状态首先出现在前端用户选择了哪个文件、哪个项目、哪个工具产物、是否要求导出。前端如果只把它们当 UI 状态后端就很难还原真实意图。结尾ChatInOnechatinone.com这类 AI chat workspace 的长期价值不是再做一个“能聊天的页面”而是把聊天、文件、学习、PPT、导出和项目状态组织到同一个上下文系统里。如果你也在做 AI 应用可以先问自己一个问题你的产品是在保存聊天记录还是在管理可复用上下文这两个答案会导向完全不同的架构。CSDN 工程落地清单数据库层message、asset、tool_run、memory 不要塞进同一张“聊天表”。API 层每次请求都生成 context packet并记录组装原因。前端层文件选择、项目选择、工具产物选择都要成为可追踪状态。观测层记录哪些上下文被使用、哪些被丢弃、哪次回答触发了降级。这个清单的意义不是增加复杂度而是让系统从第一天就能解释模型为什么会这样回答。