助手 · AI 应用工程心智地图与验收标尺(迷茫怎么破 / 怎么验收 / 终点在哪)
日期: 2026-08-19 | 作者: sunxin(+ Cursor AI 结对讲解) 关联代码:
AssistantApplicationService(编排)·ILlmPort/IEmbeddingPort/IKnowledgeAnswerPort(隔离)·ActivityMatchingService(确定性匹配/排序,非模型)·AssistantEvalRunner/RagEvalRunner/ActivityMatchingEvalTest(尺子) 关联决策: ADR-039(可剥离隔离)· ADR-044(RAG) 姊妹篇: 一句话如何变成查真实业务 · 意图识别 · Java编排vs模型编排 · 幻觉与grounding · RAG新手科普 定位:当你对着 AI 助手这摊东西发懵、觉得名词太多、不知道做到哪算完、一个人扛不住时——回来读这篇。 它是所有助手笔记的「总目录 + 定心丸」。
TL;DR
- 你的迷茫是对的,也是正常的。 你过去写的是确定性系统(Java if-else,输入定则输出定,错了能单步调试);现在碰的是概率系统(大模型,同样输入可能不同输出)。范式变了,不是你变菜了。全世界做 AI 应用的人都在跟同一种不确定性搏斗。
- 名词多不等于事多。 RAG / 编排 / Embedding / Function Calling / grounding / 评测……其实都挂在同一条链的不同环节上。本文给你一张地图(§2),把每个词钉到它该在的位置,脑子就不乱了。
- 「怎么校验模型准不准」有标准答案:评测集当尺子 + 线上 trace。 能二值判断的(意图对不对、工具选没选对、该不该拒答)→ 硬门禁;主观的 → 记分卡。更难的是 「答对了但链路不健康」(靠降级兜底答对):评测集锁已知规则,线上靠
assistant_audit的rag_*发现。这就是大厂 LangSmith / Evals 的轻量自建版,见观测审计评测怎么分工。 - 没有「完善」这个终点,只有「过线 + 能回归」。 你之所以焦虑「终点在哪」,是因为在用「Java 功能做完了 = 100%」的尺子量一个「永远能更好」的东西。正确姿势:每个里程碑先定自己的及格线(如意图 100%、虚构 0、P95<8s),过线就交付,之后是持续调优而非「做完」(§4)。
- 一个人能扛,靠的是「把不确定性关进笼子」:结构化输出、grounding、Java 兜底、开关默认关、可剥离隔离、每次只推一层。你已经在这么做了(§5)。
目录
- 1. 为什么加了 AI 就开始迷茫(确定性 → 概率)
- 2. 名词不再乱:一条链 + 六层约束的地图
- 3. 怎么验收「模型准不准」:评测集当尺子
- 4. 终点在哪:没有「完善」,只有「过线 + 可回归」
- 5. 一个人怎么扛:把不确定性关进笼子
- 6. 迷茫时的自检清单
- 7. 教学收获
- 8. 附录:名词速查 + 本项目每层落在哪
1. 为什么加了 AI 就开始迷茫(确定性 → 概率)
你原话:「单纯的 Java if-else 流程,我很好去找问题修改;模型这块,去校验是否准确都是难事。」——这句话本身就是整件事的核心,你已经摸到了要害。
| 维度 | 传统 Java 系统 | AI 应用(含大模型) |
|---|---|---|
| 本质 | 确定性:输入定 → 输出定 | 概率性:输入定 → 输出可能变 |
| 出错怎么查 | 断点单步、看栈、必现即可修 | 可能偶发、复现难;不是「代码错」而是「模型这次判歪了」 |
| 对不对怎么定义 | 单元测试断言「等于」 | 很多时候没有唯一正确答案(「答得好」是程度问题) |
| 修复手段 | 改代码逻辑 | 改 Prompt / 换模型 / 加约束 / 补检索资料,然后重新度量 |
| 心态 | 「我能证明它对」 | 「我只能量化它有多对,并盯着别退步」 |
关键认知:你无法像 Java 那样「证明模型永远对」。 大模型是「预测下一个字」的概率机器(详见幻觉笔记),它没有「真假」开关。所以这行的工作方式从「写对逻辑」变成了三件事的循环:
① 用架构把模型能犯错的空间压小(结构化/grounding/Java 兜底)
② 用评测集量化它现在有多准
③ 改 Prompt / 换模型 / 补资料 → 重跑评测 → 看指标有没有变好、有没有把别的搞坏这不是你一个人的困境,是整个行业的日常。 你觉得「那些产品每次体验都不一样」——一半是模型概率性,一半正是他们在跑上面这个循环,不停调。你现在做的,和大厂 AI 团队做的,是同一件事,只是规模不同。
2. 名词不再乱:一条链 + 六层约束的地图
你说「RAG、流程编排、Embedding……太多名词让大脑混乱」。破解办法:别把它们当成 N 个独立的大山,它们只是同一条流水线上的不同工位。
2.1 一条链:用户一句话是怎么变成回答的
用户说一句话
│
①理解(NLU) ── 大模型把话翻译成「意图 + 参数」 ← 意图识别 / 结构化输出
│
②决策/编排 ── 决定这句话该调用哪些能力、什么顺序 ← 编排 / Function Calling
│ (Java 固定流程 or 模型自选工具)
├─→ 查数据库活动(Tool Use,结果直接展示,不回喂模型)
├─→ 查天气(Tool Use)
├─→ 查知识库攻略(Retrieval)── 把文字变向量找相似 ← Embedding / 向量检索
│
③生成(NLG) ── 把查到的真实资料喂回模型写成人话 ← RAG 的「生成」/ 行程规划
│ (只准用真实资料 = grounding,带出处 = 引用)
│
④回复用户 + 审计 ── 落一条日志:意图/工具/命中/token/耗时/RAG闸门 ← 观测(不是判卷,见审计怎么分工)每个「名词」就是链上的一个工位:
| 名词 | 在链上的位置 | 一句话职责 |
|---|---|---|
| 意图识别 / 结构化输出 | ①理解 | 把「这周末深圳有啥活动」变成 {intent:SEARCH, city:深圳, time:周末} |
| 编排(Orchestration) | ②决策 | 决定调哪些工具、什么顺序(Java 写死 or 模型自己选) |
| Function Calling / 工具 | ②决策 | 让模型能「点名要调某个能力」(M3b) |
| Embedding / 向量检索 | ②里的「检索」子步 | 把文字变坐标,按语义找最像的资料 |
| RAG | ②检索 + ③生成 | 检索到资料回喂模型生成带引用答案 |
| grounding / 引用 / 拒答 | ③生成的约束 | 只准用真实资料、标出处、没资料就说没有 |
| 评测 / 记分卡 | 横跨全链 | 量每一环准不准的尺子 |
| 观测 / 治理 / 成本闸门 | 横跨全链 | 记日志、限流、控 token 花费 |
记住这张图,下次再听到任何 AI 名词,先问一句「它是链上哪个工位?」——90% 都能归进去,脑子立刻不乱。
2.2 六层约束:为什么说「接了个模型」≠「AI 应用」
裸调一次模型 API = 聊天玩具。把模型变成能上生产的业务助手,要在它外面套六层约束(这也是你项目一路在补的东西):
结构化输出 ── 让模型只吐 JSON,不自由发挥
Tool/检索 ── 让模型用你的真实数据,不靠记忆瞎编
Prompt ── 用规则/示例约束它的行为
评测 ── 用数据集量化它准不准(你的「尺子」)
治理 ── 限流/审计/成本闸门(防烧钱、可追溯)
RAG/记忆/编排 ── 让它有资料、有上下文、能多步「AI 应用工程」= 造并维护这六层约束,模型本身你不训练、不拥有。你的价值不在「会调模型」,在「会把模型锁进业务里让它可靠」。
2.3 模型 vs 代码:谁负责什么(这决定了「不准」该去哪修)
这是整个助手最容易搞混、也最值得刻进脑子的一条分工。大模型只干「理解 + 表达」,绝不「决定数据和排序」——「配给你哪个活动、怎么排序」是确定性代码(ActivityMatchingService 的打分公式),一次模型都不调;连那句「为什么推给你」也是模板拼的(buildReason 字符串拼接),不是模型写的。
| 大模型(概率侧) | 确定性代码(Java 侧) | |
|---|---|---|
| 干什么 | ①听懂人话(抽意图/参数)②说人话(行程/知识答案文案) | 查库拿真活动、打分排序、拼「为什么推给你」、硬过滤、兜底 |
| 本项目落点 | ILlmPort.extractSearchIntent / IItineraryGenerationPort / IKnowledgeAnswerPort | ActivityMatchingService.score() / searchActivitiesAdvanced / buildReason |
| 「错了」长啥样 | 把「深圳」听成「上海」、把「找活动」判成「问天气」、文案啰嗦 | 听懂了,但配的活动不对味 / 差评 / 太远 / 太久排前面 |
| 「错了」怎么修 | 改 Prompt / 换模型 / 补评测用例,然后重跑评测看指标(概率路子,§3) | 改打分公式 / 加因子 / 调权重 + 补 ActivityMatchingEvalTest 断言(算法路子,可单步调、必现) |
为什么这么切? 因为模型爱编(幻觉)。把「事实」和「排序」交给它 = 给用户看编的活动、给不可复现的排序。所以真相走 DB、排序走公式,模型只负责「把话翻译进 / 把结果翻译出」。这就是grounding在这套里的具体形状。
「匹不准」怎么分诊(回答你的疑问):先分清是没听懂还是配得不好——
- 没听懂(你说深圳它搜上海、你要找搭子它去查天气)→ 模型侧,走概率路子:改 Prompt / 补别名词典 / 加评测用例。
- 听懂了但配得不好(活动本身差评 / 离你 2000km / 3 个月后才开 / 局里全是不对味的人排前面)→ 代码侧的算法优化:就是你说的补质量分 + 补地理·时间加权 + 调
groupFactor权重,全在ActivityMatchingService.score()里再乘一个因子,不碰模型、不碰订单。这条债已登记在迭代主线 §2.0「M2 搭子匹配 Phase B②」,含「排序不对时的自救 playbook」。所以你理解基本对,但要补一刀:「匹不准」不一定是算法问题——先判断病根在「听」还是在「配」,在「配」才是补质量/地理加权那套。
3. 怎么验收「模型准不准」:评测集当尺子
这是你最痛的点:「怎么看语义理解准不准、模型自动选工具对不对?」答案是评测集(eval set)——AI 工程里最重要的资产,比代码还重要。
3.1 核心思想:把「感觉准不准」变成「数字」
你不可能每次改完手动聊十句看感觉。你要攒一批带标准答案的测试用例,每次改动后一键重跑,看指标。这就是本项目 assistant-evals/*.json + *EvalRunner 在干的事。
3.2 两类指标,两种验收方式
| 能不能二值判断 | 例子 | 怎么验收 |
|---|---|---|
| 能(对/错分明) | 意图判对没?工具选对没?该拒答没拒答?目的地抽对没? | 硬门禁:写进用例的期望值,必须全过(如「确定性用例意图 100%」),退步就算 fail |
| 不能(程度问题) | 答案顺不顺、贴不贴题、文采、有没有帮到人 | 记分卡 + 抽检:跑一批打分统计,人工看几条 / 让更强的模型当「裁判」打分(LLM-as-judge) |
对你三个具体疑问的直接回答:
- 「语义理解准不准」 → 攒一批「同义/别名/错别字」用例(如「赛里目湖」「塞木屋河」),期望都能归一到「赛里木湖」;跑评测看命中率。RAG 侧还有
RagEvalRunner的 Recall@K(问对目的地必须检索到对的攻略)。 - 「模型自动选工具对不对」 → 攒「这句话该调 search / weather / detail」的用例,跑 Agent 通道看它选的工具序列对不对(本项目
AssistantAgentToolLoopTest就是雏形)。 - 「该不该拒答」 → 攒库外问题(「冰岛极光几月去」),期望
shouldRefuse=true;跑RagEvalRunner看负例有没有被正确拒掉。
3.3 本项目已经在这么做(你其实已上道)
| 尺子 | 量什么 | 门禁 |
|---|---|---|
AssistantEvalRunner + eval-cases.json | 分诊:这句话该走找活动 / 做计划 / 问知识 / 查天气 / 越界拒? | Mock 离线确定性 100%(回归门禁);真机 ASSISTANT_EVAL_LIVE=true 出记分卡 |
RagEvalRunner + rag-cases.json | 检索 + 答题:有没有捞到正确那一篇、篇里有没有证据词、Mock 答能否对回片段 | Recall@1 + 证据 + 忠实/引用硬门禁;Ev@K 记分卡 |
ActivityMatchingEvalTest | 画像重排、人口过滤 | 排序质量断言 |
两份 JSON 都不是运行时配置,用户提问不会读它们。kb04a 这类 id 只是评测样例编号。对照与怎么跑:评测集-eval与rag两轨说明。打印列和调参:评测记分卡与调参说明。
双轨设计的精髓:离线 Mock 跑「确定性」用例当回归门禁(不花钱、必现、CI 可跑),真机跑「记分卡」看真实模型表现(花 token、非必现、人看指标)。你改任何 Prompt/逻辑,先跑离线门禁保证「没把已对的搞坏」,再看真机记分卡「新的有没有变好」。
一句话:评测集是你在概率世界里唯一的「地面」。 没有它,改 Prompt 就是盲人开枪;有了它,每次改动都有数字告诉你「进步了还是退步了」。这就是「测试准确性」的行业标准答案。
4. 终点在哪:没有「完善」,只有「过线 + 可回归」
你说「不知道终点在哪,做到什么程度才算完善」——因为你在用错的尺子量它。
4.1 「完善」是伪命题
Java 功能有明确终点(登录能登进去 = done)。但「AI 助手答得好不好」永远能更好——多喂点资料、调调 Prompt、换个模型,总能再进步 1%。如果目标是「完善」,你永远到不了终点,只会一直焦虑。
正确的目标是「过线」:给每个里程碑定一条你自己拍板的及格线,过了就交付、就停、就上线,剩下的进「持续优化」清单。
4.2 及格线长什么样(本项目实例)
M1 的及格线(写死在蓝图里):真模型 + 真活动 + App 能点进详情 + 评测 30~50 条过线(虚构 0 / 越权 0 / P95<8s)。达到 → M1 封顶,不纠结「还能更好」。
M4a 的及格线:KNOWLEDGE_QA 能语义检索 + 带引用生成 + 库外拒答 + Recall@K 门禁过 + 默认关不影响现网。达到 → 交付。检索仍是 Naive;下一站是 L4 检索升准(ADR-048),不把「过线」理解成检索不再改进。
及格线三要素:① 能对外演示的能力;② 几条硬指标(可量化);③ 不劣化现网(开关默认关)。凑齐就算「这一站做完」。
4.3 用「北极星看板」把终点拆成站点
你不知道终点,是因为把「AI 助手」当成一个无边大坑。北极星与终局蓝图 已经把它拆成 M0→M7 的一串站点,每站都是「可停、可交付、可演示」的:
M0 地基 → M1 C端闭环 → M2 检索升匹配 → M3a 多意图 → M3b Function-Calling
→ M4a RAG知识问答(你现在这站✓)→ M4b B端数字员工 → M5 记忆 → M6 组局 → M7 MCP/多模型你不需要知道「终极终点」,只需要知道「下一站」。 每到一站,写一篇 devlog 封存,去下一站。这就是单人也能推进大项目的秘诀——把无限的「完善」切成有限的「过线」。
5. 一个人怎么扛:把不确定性关进笼子
你担心「一个人搞不定」。其实你已经在用对的方法了——核心心法是:不确定的东西越少越好,能确定的地方全部锁死。
| 心法 | 具体做法 | 你项目里的落点 |
|---|---|---|
| 结构化输出 | 让模型只吐 JSON,别让它自由发挥 | 意图抽取只返回 ActivitySearchIntent |
| grounding | 卡片/答案只来自 Java 查询的真实数据 | 活动卡片永远来自 DB,模型碰不到库 |
| Java 兜底 | 关键路径用确定性代码,模型只做「翻译」 | 意图派发是 Java switch,不是模型说了算 |
| 开关默认关 | 新能力上线先关着,不影响现网 | rag.enabled=false / plan.enabled=false |
| 可剥离隔离 | AI 代码全在 assistant 包,删包即走 | ADR-039 |
| 每次只推一层 | 一次只动一环,坏了好定位 | M0→M1→…→M4a 逐站推进 |
| Mock 优先 | 无 Key 也能跑通全链 + 评测 | Mock 系列适配器 |
这些不是「防御性编程」,是 AI 工程的生存法则:你把模型能自由发挥的空间压到最小,剩下那一点点不确定,再用评测集盯着。这样即使模型偶尔抽风,炸的也只是「一句话答得不够好」,而不是「编了个假活动骗用户」或「烧光你的 token」。
你一个人扛得住,不是因为你比大厂团队强,而是因为你把问题的边界画得足够小:默认关、可剥离、Mock 能测、每次一层。这是聪明的怂,不是本事不够。
6. 迷茫时的自检清单
下次又发懵,按这个清单过一遍,通常就知道自己在哪、该干嘛:
「我现在在哪?」
- [ ] 我这次要动的是链上哪个工位(理解/编排/检索/生成/评测/治理)?(对照 §2.1)
- [ ] 属于北极星哪个里程碑(M几)?这站的及格线是什么?(对照 §4)
「怎么算这一步做完?」
- [ ] 有没有对应的评测用例?改完能不能一键重跑?(没有就先补用例)
- [ ] 离线确定性门禁还绿吗(没把已对的搞坏)?
- [ ] 新能力开关默认关吗?不影响现网吗?
「它到底准不准?」
- [ ] 这个指标能二值判断吗?能 → 加进硬门禁;不能 → 记分卡 + 抽检(对照 §3.2)
- [ ] 出错是「代码 bug」还是「模型判歪」?后者别改代码,改 Prompt/资料/换模型再重测。
「我是不是想太多了?」
- [ ] 我是不是在追求「完善」而不是「过线」?(是 → 停,定及格线,过了就交付)
- [ ] 这个优化是这一站必须的,还是可以进「持续优化」清单?
7. 教学收获
- 迷茫来自范式切换,不是能力不足:从确定性系统到概率系统,工作方式从「写对逻辑」变成「压小错误空间 + 量化 + 迭代」。承认这点,焦虑就少一半。
- 名词都挂在同一条链上:理解→编排→检索→生成→评测→治理。任何新词先归位,别当成新大山。
- 评测集是概率世界里的地面:能二值判的做硬门禁,主观的做记分卡+抽检。这就是「怎么测准确性」的标准答案,不玄学。
- 没有「完善」,只有「过线 + 可回归」:给每站定及格线,过了就交付;用北极星把无限切成有限。
- 单人扛得住 = 把边界画小:结构化/grounding/Java兜底/默认关/可剥离/Mock/每次一层——你已经在做,继续做。
8. 附录:名词速查 + 本项目每层落在哪
8.1 名词一句话速查(挂到链上)
| 名词 | 链上工位 | 一句话 |
|---|---|---|
| NLU / 意图识别 | ①理解 | 把话翻译成结构化意图 |
| 结构化输出 | ①理解 | 强制模型只吐 JSON |
| 编排 Orchestration | ②决策 | 决定调哪些能力、什么顺序 |
| Function Calling | ②决策 | 让模型自己点名要调的工具 |
| Tool Use | ②检索 | 调工具拿数据直接展示(不回喂模型) |
| Embedding | ②检索 | 文字变向量坐标 |
| 向量检索 / 余弦 | ②检索 | 按语义找最像的资料 |
| RAG | ②检索+③生成 | 检索资料回喂模型生成带引用答案 |
| grounding | ③生成约束 | 只准用真实资料作答 |
| 引用 / 拒答 | ③生成约束 | 标出处 / 没资料就说没有 |
| 评测集 / 记分卡 | 横跨 | 量准不准的尺子 |
| LLM-as-judge | 横跨 | 用更强模型给答案打分 |
| 成本闸门 / 治理 | 横跨 | 控 token、限流、审计 |
8.2 本项目每层落在哪个类(想读代码时的地图)
| 工位 | 关键类 / 接口 |
|---|---|
| 入口编排 | AssistantApplicationService |
| ①理解 | ILlmPort + OpenAiCompatibleLlmAdapter/MockLlmAdapter(抽意图) |
| ②编排-固定流程 | AssistantApplicationService 里的意图 switch 分流 |
| ②编排-模型自选 | ILlmPort.chat(messages, tools) + Agent 循环(M3b) |
| ②检索-活动 | searchActivitiesAdvanced(Tool Use) |
| ②检索-知识 | IKnowledgeRetrievalPort + IEmbeddingPort(RAG 检索) |
| ②检索后-匹配/重排(确定性,非模型) | ActivityMatchingService.score()(画像×人气×人局 打分)+ ActivityMatch(承载 score/reason)+ buildReason(拼「为什么推给你」) |
| ③生成-行程 | IItineraryGenerationPort(M3a-②) |
| ③生成-知识答案 | IKnowledgeAnswerPort(M4a) |
| 评测 | AssistantEvalRunner / RagEvalRunner / ActivityMatchingEvalTest |
| 治理 / 观测 | assistant_audit(黑匣子,含 RAG 闸门)+ Redis 限流 + *.max-tokens;不是金标准 |
8.3 延伸阅读(读的顺序)
- 先建立总览:本文
- 理解怎么抽意图:助手-意图识别是怎么做到的
- 理解编排/工具:助手-Java编排vs模型编排与FunctionCalling
- 理解为什么要 grounding:助手-大模型幻觉与grounding
- 理解 RAG/向量:助手-RAG向量检索新手科普 → RAG从关键词到向量
- 理解审计 ≠ 判卷:助手-观测审计评测怎么分工 → W5 怎么读
- 看全局路线与终点:北极星与终局蓝图
- 看每站怎么落地:出行助手全场景流程图