去哪儿 AI 面 — AI 全栈方向¶
面试形式:双机位(手机也要开摄像头),共 8 轮问答,每轮内会提一个问题并根据回答进行追问 以下内容仅根据回忆记录,未录音
1. 自我介绍¶
常规开场,简要介绍个人背景、技术栈和项目经历。
回答框架
- Who:姓名、学历、工作年限
- What:核心技术栈(如 Go/TypeScript + AI Agent)
- Highlight:1-2 个最有亮点的项目,用数据说话
- Why:为什么投这个岗位,和 AI 全栈方向的契合点
2. 语言选择(Java / TypeScript)¶
选项只有 Java 和 TypeScript 两个,后续问题会根据选择的语言侧重点展开。
选择 Java 的考察方向:JVM 原理、GC、并发编程、Spring 生态
选择 TypeScript 的考察方向:类型系统、异步模型、Node.js 运行时、前端工程化
3. 垃圾回收 GC 过程¶
3.1 内存分代模型¶
JVM 堆内存分为新生代(Young Generation)和老年代(Old Generation),新生代又细分为:
graph LR
subgraph 新生代
E[Eden 区] -->|对象优先分配| S0[Survivor 0]
E --> S1[Survivor 1]
S0 -->|Minor GC 后存活| S1
S1 -->|Minor GC 后存活| S0
end
subgraph 老年代
O[Old Generation]
end
S0 -->|年龄达到阈值| O
S1 -->|年龄达到阈值| O
E -->|大对象直接分配| O
3.2 对象分配与晋升路径¶
- 对象优先在 Eden 区分配:大多数对象朝生夕灭,Eden 区采用指针碰撞(Bump the Pointer)分配
- 大对象直接进入老年代:通过
-XX:PretenureSizeThreshold设置阈值,避免大对象在 Eden 和 Survivor 之间来回复制 - 长期存活对象晋升老年代:对象每经历一次 Minor GC 仍然存活,年龄计数器 +1,达到
-XX:MaxTenuringThreshold(默认 15)后晋升 - 动态年龄判定:Survivor 空间中相同年龄所有对象大小总和大于 Survivor 空间一半时,年龄大于等于该年龄的对象直接进入老年代
3.3 三种 GC 类型¶
| GC 类型 | 触发条件 | 回收区域 | 停顿时间 |
|---|---|---|---|
| Minor GC | Eden 区满 | 新生代 | 短(复制算法,存活对象少) |
| Major GC | 老年代空间不足 | 老年代 | 较长 |
| Full GC | 老年代满 / 方法区满 / System.gc() |
整个堆 + 方法区 | 最长 |
Full GC 触发的常见场景
- 老年代空间不足且无法扩展
- 方法区(元空间)不足
- CMS GC 出现 Concurrent Mode Failure
- 显式调用
System.gc()(可通过-XX:+DisableExplicitGC禁用)
3.4 各代回收算法¶
新生代 — 复制算法(Copying):
- Eden:S0:S1 = 8:1:1(默认,通过
-XX:SurvivorRatio调整) - Minor GC 时将 Eden 和一个 Survivor 中存活对象复制到另一个 Survivor
- 优点:无内存碎片,分配效率高
- 缺点:浪费 10% 的 Survivor 空间;如果 Survivor 空间不够,需要依赖老年代的分配担保(Handle Promotion)
老年代 — 标记-清除(Mark-Sweep):
- 标记所有存活对象,清除未标记对象
- 优点:不需要移动对象
- 缺点:产生内存碎片,大对象分配可能触发 Full GC
老年代 — 标记-整理(Mark-Compact):
- 标记存活对象后,将所有存活对象向一端移动,然后清理边界外的内存
- 优点:无内存碎片
- 缺点:移动对象需要更新引用,STW 时间更长
3.5 经典垃圾收集器¶
| 收集器 | 算法 | 区域 | 特点 |
|---|---|---|---|
| Serial | 复制 / 标记-整理 | 新生代 / 老年代 | 单线程,Client 模式默认 |
| ParNew | 复制 | 新生代 | Serial 的多线程版本,配合 CMS |
| Parallel Scavenge | 复制 | 新生代 | 关注吞吐量(-XX:MaxGCPauseMillis) |
| CMS | 标记-清除 | 老年代 | 低停顿,但有碎片 |
| G1 | 分区 + 标记-整理 | 整个堆 | 可预测停顿,JDK 9+ 默认 |
| ZGC | 着色指针 + 读屏障 | 整个堆 | 停顿 < 10ms,JDK 15+ 生产就绪 |
4. 三色标记的第一阶段 STW 在干什么¶
4.1 三色标记法基础¶
三色标记法是并发 GC 的核心算法,将堆中对象分为三类:
| 颜色 | 含义 |
|---|---|
| 白色 | 尚未被扫描的对象。GC 结束后仍为白色的对象将被回收 |
| 灰色 | 已被扫描,但其引用的子对象尚未全部扫描完成 |
| 黑色 | 已被扫描,且其所有子引用都已扫描完成 |
4.2 CMS 的四阶段与第一阶段 STW¶
CMS(Concurrent Mark Sweep)的完整 GC 流程:
graph TD
A[初始标记 Initial Mark<br/>⚡ STW] --> B[并发标记 Concurrent Mark]
B --> C[重新标记 Remark<br/>⚡ STW]
C --> D[并发清除 Concurrent Sweep]
第一阶段「初始标记」(Initial Mark)的 STW 做了什么:
-
枚举 GC Roots 直接可达的对象:扫描以下根节点集合
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性(
static字段)引用的对象 - 方法区中常量(
final static)引用的对象 - 本地方法栈中 JNI(Native 方法)引用的对象
- JVM 内部引用(如基本类型对应的 Class 对象、常驻异常对象、系统类加载器)
- 被同步锁(
synchronized)持有的对象
-
标记这些直接可达对象为灰色:只标记 GC Roots 的直接引用,不递归遍历整个引用链
-
建立并发标记的起点:为后续并发标记阶段提供一个一致的、从 GC Roots 出发的起始快照
为什么需要 STW
如果初始标记不 STW,在扫描 GC Roots 的过程中,应用线程可能修改引用关系(比如栈帧中的局部变量被赋值为新对象),导致某些 GC Roots 引用的对象被遗漏。初始标记只需要扫描 GC Roots,存活对象少,STW 时间通常很短(毫秒级)。
4.3 并发标记期间的问题与写屏障¶
并发标记阶段应用线程和 GC 线程同时运行,可能产生两个问题:
问题一:白色对象被黑色对象新增引用(漏标)
问题二:灰色对象删除了对白色对象的引用(误保留不算问题,只是多活一轮)
CMS 的解决方案 — 增量更新(Incremental Update):
通过写屏障(Write Barrier)在引用赋值时记录变化:
// 写屏障伪代码:当黑色对象新增引用时
void write_barrier(Object* slot, Object* new_ref) {
if (current_phase == CONCURRENT_MARK) {
if (is_black(slot_object) && is_white(new_ref)) {
remark_set.add(slot); // 记录这个写入
}
}
*slot = new_ref; // 执行实际赋值
}
重新标记阶段(Remark,第二次 STW)处理这些记录,将被黑色对象新引用的白色对象重新扫描。
G1 的解决方案 — SATB(Snapshot At The Beginning):
通过写屏障在引用被覆盖前记录旧值:
// SATB 写屏障伪代码
void write_barrier(Object* slot, Object* old_ref, Object* new_ref) {
if (current_phase == CONCURRENT_MARK) {
satb_queue.push(old_ref); // 记录被覆盖的旧引用
}
*slot = new_ref;
}
SATB 保证并发标记开始时的引用快照中所有存活对象都会被标记,可能多标记(浮动垃圾),但不会漏标。
4.4 G1 与 CMS 的关键差异¶
| 维度 | CMS | G1 |
|---|---|---|
| 算法 | 标记-清除(有碎片) | 标记-整理(无碎片) |
| 区域 | 连续的新生代 / 老年代 | 将堆划分为等大的 Region |
| 并发标记策略 | 增量更新(Incremental Update) | SATB(Snapshot At The Beginning) |
| 停顿预测 | 不支持 | 支持(-XX:MaxGCPauseMillis) |
| Mixed GC | 不支持 | 支持(选择回收价值最高的 Region) |
| 碎片处理 | Full GC 时压缩 | 每次 GC 都做整理 |
5. 最有成就感的项目¶
STAR 法则回答框架
- Situation:项目的背景和业务上下文
- Task:你负责的具体任务和目标
- Action:你采取的技术方案和关键决策
- Result:量化的成果(性能提升 X%、成本降低 X%、效率提升 X 倍)
回答要点:
- 背景与目标:为什么做这个项目,要解决什么业务痛点,目标指标是什么
- 技术选型与架构设计:为什么选择当前方案,对比过哪些替代方案,取舍的依据
- 关键难点与突破:遇到了什么技术挑战(如性能瓶颈、一致性问题、高可用保障),如何分析和解决的
- 最终成果:用数据说话,如 QPS 从 X 提升到 Y、延迟从 Xms 降低到 Yms、故障率降低 X%
6. AI 提效成果¶
在项目开发过程中,AI 工具对效率的提升,需要有具体的量化数据和使用场景:
6.1 具体提效场景¶
| 场景 | 传统方式耗时 | AI 辅助耗时 | 提效倍数 |
|---|---|---|---|
| 重复性 CRUD 代码 | 2h | 20min | 6x |
| 单元测试编写 | 1h | 15min | 4x |
| 代码 Review(首轮) | 30min | 5min | 6x |
| 文档编写 | 2h | 30min | 4x |
| 问题排查与定位 | 1h | 15min | 4x |
6.2 提效方法论¶
- Context 工程:给 AI 提供精准的上下文(项目结构、依赖关系、编码规范),而非泛泛的提示
- 任务拆解:将大任务拆成小的、可验证的子任务,每个子任务交给 AI 完成后人工验收
- 迭代式开发:AI 生成初稿 → 人工 Review → 反馈修改 → 再次验收,而非一步到位
- 模板化 Prompt:对高频场景(如 API 接口、数据模型)沉淀为可复用的 Prompt 模板
7. 如何确保 AI 生成格式化内容的可靠性¶
生成 JSON 等结构化内容时,确保键的准确性和嵌套层级的正确性:
7.1 技术方案对比¶
| 方案 | 原理 | 可靠性 | 灵活性 | 延迟 |
|---|---|---|---|---|
| JSON Mode | 模型强制输出合法 JSON | 中(格式对,内容可能错) | 高 | 低 |
| Structured Output | 基于 JSON Schema 约束输出 | 高 | 中 | 低 |
| Function Calling | 模型输出工具调用参数 | 高 | 中 | 低 |
| 分步生成 | 先生成骨架再填充 | 高 | 高 | 高 |
| 后处理校验 + 重试 | 解析校验失败则重试 | 中 | 高 | 不确定 |
7.2 Structured Output 实现原理¶
OpenAI 的 Structured Output 基于 Constrained Decoding(受限解码):
- 将 JSON Schema 转换为 有限状态自动机(FSA) 或 上下文无关文法(CFG)
- 在每一步 token 生成时,根据当前状态和 schema 约束,屏蔽不合法的 token
- 模型只能从合法 token 中采样,保证输出 100% 符合 schema
graph LR
A[JSON Schema] --> B[转换为 CFG/FSA]
B --> C[受限解码引擎]
D[模型输出 logits] --> C
C --> E[屏蔽非法 token]
E --> F[采样合法 token]
F --> G[符合 Schema 的 JSON]
7.3 实践建议¶
- 提供严格的 JSON Schema:定义
required字段、enum约束、additionalProperties: false - Few-shot 示例:提供 2-3 个目标格式的完整示例,让模型学习结构模式
- 分步生成复杂结构:对于深层嵌套 JSON,先生成顶层结构,再逐层填充
- 后处理兜底:即使用了 Structured Output,仍需对内容语义进行校验(格式对不等于内容对)
8. Agent 场景与目标达成¶
8.1 Agent 核心架构模式¶
graph TD
U[用户输入] --> P[Planning 规划]
P --> E[Execution 执行]
E --> O[Observation 观察]
O -->|未完成| P
O -->|已完成| R[最终结果]
E --> T1[工具调用 1]
E --> T2[工具调用 2]
E --> T3[工具调用 N]
主流 Agent 架构模式:
| 模式 | 原理 | 适用场景 |
|---|---|---|
| ReAct | Reasoning + Acting 交替进行 | 通用任务,步骤不确定 |
| Plan-and-Execute | 先规划完整计划,再逐步执行 | 复杂任务,需要全局规划 |
| Reflexion | 执行后自我反思,失败时调整策略 | 需要试错的任务 |
| Multi-Agent | 多个 Agent 协作,各司其职 | 复杂系统,需要分工 |
| Critic/Verifier | 执行 Agent + 审查 Agent | 需要高可靠性的任务 |
8.2 ReAct 模式详解¶
Thought: 我需要查询用户的订单信息,首先需要调用订单查询 API
Action: query_orders(user_id="12345", status="pending")
Observation: 返回了 3 笔待处理订单
Thought: 我需要对这 3 笔订单进行金额汇总
Action: calculate_total(orders=[...])
Observation: 总金额为 ¥15,000
Thought: 任务已完成,可以给出最终回答
Answer: 用户共有 3 笔待处理订单,总金额 ¥15,000
8.3 工具链设计原则¶
- 单一职责:每个工具只做一件事,通过组合实现复杂功能
- 幂等性:相同输入产生相同结果,支持安全重试
- 错误处理:工具返回结构化错误信息,Agent 可据此调整策略
- 权限控制:敏感操作(如删除、支付)需要人工确认(Human-in-the-Loop)
9. Agent 长期记忆管理¶
9.1 记忆分层架构¶
graph TD
subgraph 短期记忆
W[工作记忆 Working Memory<br/>当前会话上下文]
end
subgraph 中期记忆
E[情景记忆 Episodic Memory<br/>历史交互记录]
end
subgraph 长期记忆
S[语义记忆 Semantic Memory<br/>知识、偏好、经验]
P[程序性记忆 Procedural Memory<br/>技能和操作流程]
end
W -->|会话结束| E
E -->|提炼总结| S
S -->|指导行为| P
9.2 记忆淘汰机制¶
LRU(Least Recently Used)— 最近最少使用:
- 实现:双向链表 + 哈希表,O(1) 访问和淘汰
- 优点:适合有时间局部性的场景
- 缺点:不考虑访问频率,偶尔访问一次就能"续命"
LFU(Least Frequently Used)— 最不经常使用:
- 实现:最小堆或双链表 + 频率桶
- 优点:考虑了访问频率,高频记忆不易被淘汰
- 缺点:存在"历史热点"问题,早期高频但后期不再使用的记忆难以淘汰
实际推荐 — 混合策略:
recency:时间衰减函数,如e^(-λt),t 为距上次访问的时间frequency:访问频率,可用 log 平滑importance:人工标注的重要性等级(如用户明确要求记住的 = 1.0,自动生成的 = 0.5)α, β, γ:权重参数,根据场景调优
9.3 记忆存储结构¶
{
"memory_id": "mem_001",
"content": "用户偏好使用 Go 语言开发后端服务",
"type": "semantic",
"source": "user_explicit",
"created_at": "2026-01-15T10:30:00Z",
"last_accessed": "2026-08-30T14:20:00Z",
"access_count": 42,
"importance": 0.9,
"confidence": 0.95,
"tags": ["preference", "language", "backend"],
"embedding": [0.12, -0.34, ...]
}
10. 记忆部分冲突处理¶
当多条记忆之间存在部分冲突(如两条记忆描述同一事物但细节不一致):
10.1 知识图谱辅助判断¶
将记忆中的实体和关系组织为知识图谱:
graph LR
M1[记忆1: 用户使用 MySQL 8.0] -->|存储| DB[(数据库)]
M2[记忆2: 用户使用 PostgreSQL 15] -->|存储| DB
M3[记忆3: 项目 QData 使用 MySQL] -->|项目| P[QData]
M4[记忆4: 项目 OpenAPI 使用 PostgreSQL] -->|项目| P2[OpenAPI]
DB -->|推理| R[结论: 用户在不同项目中使用不同数据库]
10.2 冲突消解策略¶
- 上下文化:判断冲突是否因上下文不同导致(不同项目、不同时期),若上下文不同则两条记忆都保留
- 时间衰减:更新的记忆通常更准确,赋予更高权重
- 来源可信度:用户明确告知 > 从对话中推断 > 自动生成
- 置信度加权:每条记忆有置信度评分,冲突时取置信度更高的
def resolve_conflict(mem_a, mem_b):
# 1. 检查是否真正冲突(上下文不同则不冲突)
if mem_a.context != mem_b.context:
return [mem_a, mem_b] # 都保留
# 2. 时间加权
time_score_a = exp(-lambda_ * (now - mem_a.created_at))
time_score_b = exp(-lambda_ * (now - mem_b.created_at))
# 3. 来源可信度
source_weight = {"user_explicit": 1.0, "inferred": 0.7, "auto": 0.5}
# 4. 综合评分
score_a = time_score_a * source_weight[mem_a.source] * mem_a.confidence
score_b = time_score_b * source_weight[mem_b.source] * mem_b.confidence
return mem_a if score_a > score_b else mem_b
10.3 无法自动消解时¶
当置信度差异不大(如 score 差值 < 0.1),主动向用户澄清:
"我发现两条关于数据库选择的记忆存在冲突: - 记忆 A(2026-01-15):你提到项目使用 MySQL - 记忆 B(2026-06-20):你提到项目使用 PostgreSQL
请问哪条是正确的,或者它们分别适用于不同的场景?"
11. 两条记忆完全冲突¶
当两条记忆完全矛盾(同一实体、同一上下文、相反的结论),根据环境风险等级采取不同策略:
11.1 风险等级分类¶
| 风险等级 | 场景示例 | 策略 |
|---|---|---|
| 危险(Critical) | 医疗用药、金融操作、生产环境配置、权限变更 | 不做自动选择,暴露冲突,人工决策 |
| 安全(Safe) | 代码风格偏好、文档格式、非关键配置 | 按优先级规则自动采纳 |
11.2 危险环境处理¶
graph TD
A[检测到完全冲突] --> B{环境风险等级?}
B -->|危险| C[停止自动决策]
C --> D[输出两条冲突记忆]
D --> E[说明冲突内容和来源]
E --> F[等待人工决策]
F --> G[将决策结果写入记忆]
原则:宁可暴露冲突,也不做错误的自动选择。在医疗、金融、生产环境等高风险场景下,错误的自动选择可能造成不可逆的损失。
11.3 安全环境处理¶
可配置的优先级规则体系:
conflict_resolution:
rules:
- dimension: time
strategy: newer_wins # 新记忆优先
weight: 0.4
- dimension: source
priority:
- user_explicit # 用户明确告知
- user_confirmed # 用户确认过的推断
- inferred # 从对话推断
- auto_generated # 自动生成
weight: 0.4
- dimension: access_count
strategy: higher_wins # 高频访问优先
weight: 0.2
fallback: ask_user # 规则无法判定时询问用户
11.4 冲突检测机制¶
- 语义相似度:使用 embedding 计算余弦相似度,相似度 > 0.8 的记忆对进入冲突检测
- 矛盾检测:对相似记忆进行 NLI(自然语言推理)判断,如果蕴含关系为"矛盾"则标记为冲突
- 定期巡检:后台定期扫描记忆库,检测新增记忆与已有记忆的冲突
12. 如何评价是否达成目标¶
12.1 Critic 架构详解¶
graph TD
U[用户目标] --> A[执行 Agent]
A -->|输出| C[Critic Agent]
C -->|评分 + 反馈| J{达标?}
J -->|是| R[返回最终结果]
J -->|否| F[反馈修改建议]
F --> A
Critic Agent 的评估维度:
| 维度 | 评分标准 | 权重 |
|---|---|---|
| 准确性 | 输出内容是否事实正确 | 0.3 |
| 完整性 | 是否覆盖了所有要求 | 0.25 |
| 格式合规 | 是否符合预期格式和规范 | 0.15 |
| 效率 | 是否用最少步骤完成 | 0.1 |
| 可读性 | 输出是否清晰易懂 | 0.1 |
| 安全性 | 是否包含有害或不当内容 | 0.1 |
12.2 多层评估体系¶
graph LR
subgraph 自动评估
L1[格式校验 Schema Check]
L2[单测通过率]
L3[Lint 零告警]
end
subgraph AI 评估
L4[Critic Agent 评分]
L5[多 Agent 投票]
L6[Adversarial Verify]
end
subgraph 人工评估
L7[Code Review]
L8[用户验收]
end
L1 --> L4 --> L7
L2 --> L5 --> L8
L3 --> L6
12.3 打分量化实现¶
class GoalEvaluator:
def evaluate(self, task, output) -> dict:
scores = {}
# 自动评估
scores["format"] = self.check_format(output, task.schema) # 0-1
scores["tests"] = self.run_tests(output, task.test_cases) # 0-1
scores["lint"] = self.run_lint(output, task.lint_config) # 0-1
# AI 评估
scores["accuracy"] = self.critic.evaluate(
prompt=f"评估以下输出的准确性,目标:{task.goal},输出:{output}",
rubric=ACCURACY_RUBRIC
) # 1-5
scores["completeness"] = self.critic.evaluate(
prompt=f"评估以下输出的完整性,要求:{task.requirements},输出:{output}",
rubric=COMPLETENESS_RUBRIC
) # 1-5
# 加权总分
weights = {"format": 0.15, "tests": 0.2, "lint": 0.1,
"accuracy": 0.3, "completeness": 0.25}
total = sum(scores[k] * weights[k] for k in weights)
return {"scores": scores, "total": total, "passed": total >= 0.8}
13. 如何应对 AI 幻觉¶
13.1 幻觉的分类¶
| 类型 | 描述 | 示例 |
|---|---|---|
| 事实性幻觉 | 生成与客观事实不符的内容 | 编造不存在的 API 或函数 |
| 忠实性幻觉 | 生成与输入上下文不一致的内容 | 检索到文档说 A,回答说 B |
| 推理幻觉 | 逻辑推理过程出错 | 数学计算错误、因果倒置 |
| 指令幻觉 | 偏离用户指令要求 | 要求生成 JSON 却输出 YAML |
13.2 Context 工程¶
核心思想:减少模型需要"猜测"的空间,提供精准、充分的上下文。
graph TD
subgraph Context 构成
S[System Prompt<br/>角色、规则、输出格式]
K[Knowledge Base<br/>项目文档、API 文档、规范]
H[History<br/>对话历史、工具调用结果]
R[Retrieved Context<br/>RAG 检索的相关文档]
end
S --> LLM
K --> LLM
H --> LLM
R --> LLM
LLM --> O[输出]
关键实践:
- 项目规范注入:将 CLAUDE.md 中的编码规范、命名约定、返回格式等注入 system prompt
- 实时上下文获取:通过工具调用获取最新的代码状态、文件内容,而非依赖模型的参数记忆
- 约束明确化:明确告诉模型"不要猜测,不确定时说不知道"
13.3 Sub-agent 评估(Adversarial Verify)¶
引入独立的审查 Agent 对主 Agent 的输出进行事实核查:
graph LR
A[主 Agent 输出] --> V1[验证 Agent 1<br/>事实核查]
A --> V2[验证 Agent 2<br/>逻辑检查]
A --> V3[验证 Agent 3<br/>一致性检查]
V1 --> J{多数通过?}
V2 --> J
V3 --> J
J -->|是| R[接受输出]
J -->|否| F[要求重新生成]
验证 Prompt 示例:
你是一个严格的事实核查员。请验证以下输出中的每个事实声明:
1. 检查是否存在编造的函数名、API、类名
2. 检查代码逻辑是否正确
3. 检查与提供的上下文是否一致
对于每个声明,给出 verdict: CONFIRMED / REFUTED / UNCERTAIN
13.4 Hook 机制¶
在关键节点插入自动化检查:
graph TD
A[AI 生成代码] --> H1[Pre-commit Hook]
H1 -->|类型检查| C1{通过?}
C1 -->|是| H2[Lint Hook]
C1 -->|否| F1[修复]
H2 -->|代码风格| C2{通过?}
C2 -->|是| H3[单测 Hook]
C2 -->|否| F2[修复]
H3 -->|测试通过| C3{通过?}
C3 -->|是| M[合并]
C3 -->|否| F3[修复]
13.5 Skill 补充背景信息¶
在未知领域,通过 Skill 注入领域知识:
- 场景:AI 不了解项目的特定技术栈或业务领域
- 方案:将领域文档、最佳实践、常见陷阱封装为 Skill,在相关任务时自动加载
- 效果:AI 基于真实的领域知识回答,而非猜测
13.6 检索增强生成(RAG)¶
graph LR
Q[用户问题] --> E[Embedding]
E --> S[向量检索]
S -->|Top-K 文档| C[Context 组装]
C --> LLM[大模型]
LLM --> A[基于文档的回答]
降低幻觉的关键:回答基于检索到的实际文档,而非模型的参数记忆。模型的角色从"知识库"转变为"文档阅读理解"。
14. 如何遵守项目规范¶
14.1 规范的层次结构¶
graph TD
subgraph 第一层: 全局规范
G1[语言规范 Go/TS]
G2[通用工程规范]
end
subgraph 第二层: 项目规范
P1[命名约定]
P2[返回类格式]
P3[错误处理模式]
P4[目录结构]
end
subgraph 第三层: 团队规范
T1[Git 提交规范]
T2[Code Review 标准]
T3[文档要求]
end
G1 --> P1 --> T1
G2 --> P2 --> T2
G2 --> P3 --> T3
14.2 Claude Code 的规范实践¶
CLAUDE.md 配置示例:
# 项目规范
## 代码风格
- Go: 遵循 Uber Go Style Guide
- TypeScript: 使用 ESLint + Prettier,严格模式
## 命名规范
- 接口以 I 开头:IUserService
- 错误类型以 Error 结尾:NotFoundError
- 数据库模型以 Model 结尾:UserModel
## 返回类格式
统一使用 Result<T> 包装:
{
"code": 0,
"message": "success",
"data": T
}
## Git 提交
使用 Conventional Commits 格式
14.3 软件工程实践约束¶
瀑布模型(Waterfall)在 AI 辅助开发中的应用:
- 需求分析:AI 辅助梳理需求文档、生成用户故事
- 设计:AI 辅助生成架构设计、数据库 ER 图、API 文档
- 实现:AI 辅助生成代码,但严格遵循设计文档
- 测试:AI 辅助生成单元测试、集成测试
- 部署:AI 辅助生成 CI/CD 配置、部署脚本
- 维护:AI 辅助日志分析、问题排查
关键点:每个阶段都有明确的输入和输出标准,AI 在每个阶段的工作需要经过人工验收才能进入下一阶段。
14.4 自动化检查兜底¶
| 检查类型 | 工具示例 | 作用 |
|---|---|---|
| 类型检查 | tsc --noEmit、go vet |
编译期错误捕获 |
| 代码风格 | ESLint、golangci-lint | 统一代码风格 |
| 安全扫描 | gosec、npm audit | 安全漏洞检测 |
| 测试覆盖 | go test -cover、Jest |
确保测试覆盖 |
| 提交规范 | commitlint | Git 提交信息规范 |
15. AI 工具选择与可靠性保障¶
15.1 AI 工具生态¶
| 工具 | 定位 | 核心优势 | 适用场景 |
|---|---|---|---|
| Claude Code | CLI Agent | 深度代码理解、工具调用、多文件编辑 | 复杂编码任务、项目级重构 |
| Cursor | IDE 集成 | 内联补全、上下文感知 | 日常编码、快速原型 |
| GitHub Copilot | 代码补全 | 广泛的语言支持、低延迟 | 代码补全、单元测试 |
| ChatGPT/Claude | 通用对话 | 强推理、多模态 | 技术方案讨论、文档编写 |
15.2 Harness(执行框架)¶
Harness 是管理 AI Agent 执行的核心框架:
graph TD
subgraph Harness 核心组件
PM[Permission Manager<br/>权限管理]
CM[Context Manager<br/>上下文管理]
TM[Tool Manager<br/>工具管理]
SM[State Manager<br/>状态管理]
HM[Hook Manager<br/>钩子管理]
end
U[用户输入] --> HM
HM --> CM
CM --> LLM[大模型]
LLM --> TM
TM -->|工具调用| PM
PM -->|授权| EX[执行]
EX --> SM
SM -->|状态更新| CM
Harness 的关键职责:
- 权限管理:哪些操作需要用户确认,哪些可以自动执行
- 上下文管理:维护对话历史、工具结果、文件状态
- 工具管理:注册、调用、结果处理
- 状态管理:任务状态、进度追踪、错误恢复
- Hook 管理:在关键节点插入自定义逻辑
15.3 可靠性保障体系¶
graph TD
subgraph 预防
H1[明确的目标定义]
H2[详细的规范约束]
H3[充分的上下文]
end
subgraph 检测
D1[Sub-agent 交叉验证]
D2[Hook 自动检查]
D3[Critic 评分]
end
subgraph 纠正
F1[自动重试]
F2[人工介入]
F3[回滚机制]
end
H1 --> D1 --> F1
H2 --> D2 --> F2
H3 --> D3 --> F3
具体措施:
- 达成目标的标准:在任务开始前明确定义"完成"的条件(Definition of Done),避免主观判断
- Hooks 机制:
preToolUse:工具调用前检查参数合理性postToolUse:工具调用后验证结果正确性preCommit:提交前运行 lint、测试、类型检查
- Sub-agent 审查:独立的子 Agent 从不同角度(正确性、安全性、性能)验证输出
- Code Review:AI 生成的代码必须经过人工 Review,重点关注边界条件和异常处理
- 渐进式信任:从简单任务开始验证 AI 能力,逐步增加任务复杂度
15.4 工具选择原则¶
- 能力匹配:工具的核心能力要与任务需求匹配
- 可控性:优先选择有良好权限控制和审计能力的工具
- 可组合性:工具之间可以组合使用,形成完整的工具链
- 成本效益:在质量和成本之间取得平衡
面试总结¶
这次去哪儿的 AI 面试整体考察深度不错,涵盖了:
| 考察维度 | 题目 | 核心考察点 |
|---|---|---|
| 语言基础 | GC 过程、三色标记 | JVM 底层原理理解深度 |
| 项目经验 | 有成就感的项目、AI 提效 | 实战经验和量化思维 |
| AI 工程实践 | 结构化输出、Agent 设计 | AI 集成的技术深度 |
| 记忆管理 | 记忆存储、冲突处理 | 系统设计和边界处理 |
| 可靠性工程 | 幻觉应对、规范遵守、工具保障 | 工程化思维和质量意识 |
核心考察点在于 AI 工程化能力 — 不只是会用 AI,而是如何可靠地将 AI 集成到实际项目中。
面试准备建议
- GC 和三色标记是 Java 面试的高频考点,需要理解到源码级别
- AI 相关问题需要有实际项目经验支撑,不能只谈理论
- 记忆管理和冲突处理是 Agent 系统的核心难点,需要有具体的方案设计
- 可靠性保障是区分"会用 AI"和"能工程化 AI"的关键