AI 应用开发Ⅱ

Ethan
2025-04-06 发布 / 正在检测是否收录...

1. 项目中 RAG 流程是什么?

标准 RAG 流程(五步)

用户提问
   ↓
① Query 处理(改写/扩展/分解)
   ↓
② 检索(Retrieval)
   ↓
③ 重排序(Rerank)
   ↓
④ 上下文拼装(Context Packing)
   ↓
⑤ 生成(Generation)
   ↓
最终回答

各步骤详解

步骤说明
① Query 处理对用户原始问题做 Query Rewrite(改写为更适合检索的表述)、Query Expansion(扩展同义词)、Query Decomposition(复杂问题拆分为多个子问题)
② 检索通过向量检索(Embedding 相似度)+ 关键词检索(BM25)混合召回,从知识库中找到相关文档块
③ Rerank用 Cross-Encoder 等模型对召回结果做精排,过滤掉低相关性内容,保留 Top-K
④ 上下文拼装将筛选后的文档块按一定策略拼装进 Prompt,加入来源标注
⑤ 生成LLM 基于检索到的证据生成回答,要求"只根据证据回答,无证据就说不确定"

面试回答模板

我们项目的 RAG 流程是:首先对用户 Query 做改写和扩展,然后通过混合检索(向量 + BM25)从知识库召回相关文档,再用 Reranker 做精排,最后把筛选后的上下文拼入 Prompt 让 LLM 生成回答。同时会要求模型只基于检索到的证据回答,没有匹配就明确告知不确定。

2. RAG 切块太小导致上下文语义断裂,如何处理?

问题本质

切块(Chunk)太小会导致:一句话被拆成两块,单独看每块都不完整,检索到的内容丢失上下文,模型无法理解完整语义。

解决方案(五种)

方案说明
① 增大切块大小将 chunk_size 从 256 提升到 512-1024 tokens,overlap 从 50 提升到 100-200 tokens
② 递归切分不按固定字符数切,而是按语义边界递归切分:先按章节 → 段落 → 句子,优先在大边界处断开
③ 滑动窗口 + 重叠相邻 chunk 之间保留一定比例的重叠(overlap),确保边界信息不丢失
④ 父子块策略(Parent-Child)小块用于检索(精准匹配),命中后返回其所在的大父块给模型(保留上下文)
⑤ 语义切分用 Embedding 计算相邻句子的语义相似度,在相似度骤降处切分,保证每块语义完整

推荐方案:父子块策略

文档
├── 父块 A(1024 tokens)← 检索命中后,把整个父块送给 LLM
│   ├── 子块 A1(256 tokens)← 用于向量检索
│   ├── 子块 A2(256 tokens)
│   └── 子块 A3(256 tokens)
└── 父块 B(1024 tokens)
    ├── 子块 B1(256 tokens)
    └── 子块 B2(256 tokens)
用小块做精准检索,用大块保留上下文。兼顾检索精度和语义完整性。

3. 文档指代消解如何做?

问题本质

文档中常见指代现象,例如:

  • "该公司2025年营收增长30%。净利润也大幅提升。" — "其"指代"该公司"
  • "张三负责前端开发。使用了 React 框架。" — "他"指代"张三"

如果直接切块,"其净利润大幅提升"单独拿出来不知道"其"是谁,检索时也无法通过"其"匹配到"该公司"。

解决方案

方案说明
① 预处理阶段做指代消解在文档入库前,用 LLM 或 NLP 工具(如 spaCy、HanLP)识别代词并替换为实际实体。"其净利润" → "该公司净利润"
② 扩大上下文窗口切块时保留足够上下文,让代词和它指代的实体在同一块内
③ 检索时做 Query 扩展用户问"该公司利润"时,扩展为"该公司 该公司利润 净利润",提高召回率
④ 元数据标注在每个 chunk 上附加文档标题、章节标题、实体标签等元数据,辅助消解

面试回答模板

指代消解主要在文档预处理阶段做。入库前用 LLM 或 NLP 工具识别代词并替换为实际实体,比如"其"替换为"该公司"。同时配合父子块策略,确保切块时保留足够上下文,让代词和指代对象在同一块内。

4. LangChain 中 Handoff 如何设计流程流转?

Handoff 是什么

Handoff(交接)是 LangGraph 中多 Agent 协作的核心机制,指一个 Agent 将任务控制权交给另一个 Agent。

设计模式

模式一:条件路由(Conditional Routing)

from langgraph.graph import StateGraph

def router(state):
    """根据当前状态决定下一步由谁处理"""
    if state["intent"] == "code":
        return "coder_agent"
    elif state["intent"] == "research":
        return "researcher_agent"
    else:
        return "general_agent"

graph = StateGraph(AgentState)
graph.add_node("planner", planner_agent)
graph.add_node("coder_agent", coder_agent)
graph.add_node("researcher_agent", researcher_agent)

# 条件边:planner 根据意图路由到不同 Agent
graph.add_conditional_edges("planner", router)

模式二:Tool-based Handoff

每个 Agent 作为一个 Tool 暴露给其他 Agent:

# Agent A 把 Agent B 封装成工具
tools = [
    Tool(
        name="delegate_to_coder",
        func=coder_agent.run,
        description="将编码任务交给 Coder Agent"
    ),
    Tool(
        name="delegate_to_reviewer",
        func=reviewer_agent.run,
        description="将审查任务交给 Reviewer Agent"
    )
]

模式三:Supervisor 模式

由一个 Supervisor Agent 统一调度:

Supervisor
  ├── 判断任务类型
  ├── 分派给对应 Agent
  ├── 收集结果
  └── 决定是否需要进一步处理

面试回答模板

LangChain/LangGraph 中的 Handoff 主要有三种设计:条件路由通过状态判断直接跳转到对应 Agent;Tool-based Handoff 把每个 Agent 封装成工具供其他 Agent 调用;Supervisor 模式由一个主 Agent 统一调度。生产中常用 Supervisor 模式,因为 Harness 可以在 Supervisor 层做预算控制、权限校验和失败处理。

5. 主流 Agent 架构你是如何理解的?

六种主流架构

架构核心思想适用场景
ReAct思考 → 行动 → 观察 → 循环通用任务,最经典
Plan-and-Execute先规划完整计划,再逐步执行长任务、复杂任务
Router先分类意图,再路由到对应处理链多能力分发
Workflow + Agent固定流程中嵌入局部自主决策线上业务最常用
Supervisor主 Agent 拆解任务,分派给子 Agent多 Agent 协作
ReflectionAgent 生成后自我反思、修正需要高质量输出

ReAct 架构图

用户提问 → Thought(思考下一步)→ Action(调用工具)→ Observation(观察结果)→ Thought → ... → 最终回答

Plan-and-Execute 架构图

用户提问 → Planner(生成计划:Step1, Step2, Step3)→ Executor(逐步执行)→ Replanner(根据结果调整计划)→ 最终回答

核心理解

Agent 架构的本质是控制流的设计。ReAct 是最简单的循环控制;Plan-and-Execute 把"想"和"做"分离;Router 做意图分发;Supervisor 做任务编排。生产中往往不是单一架构,而是混合使用:顶层用 Supervisor 编排,子任务用 ReAct 或 Plan-and-Execute 执行,固定环节用 Workflow 保证稳定性。

6. 最新技术(Harmes Agent、OpenClaw、Skills)

Harmes Agent(Multi-Agent Harness)

参考上一篇笔记《生产级Multi-Agent-Harness设计全拆解》:

  • Agent 的"操作系统",负责编排、调度、记忆、状态、工具治理、预算控制、可观测性
  • 核心原则:Agent 负责局部智能,Harness 负责全局控制
  • 五大模块:架构编排、工具治理、状态与记忆、评估体系、成本控制

OpenClaw(Claude Code / Cursor 底层架构)

OpenClaw 指的是 AI Coding Agent 的底层执行框架,核心特点:

特性说明
工具调用Agent 通过调用 Read、Edit、Bash、Grep 等工具操作代码库
上下文管理自动压缩历史对话,保留关键信息
权限控制工具调用需要用户授权(allowlist / dangerously-skip-permissions)
多模型路由不同任务使用不同模型(Haiku 做快速判断,Opus 做复杂推理)

Skills(技能系统)

Skills 是 Agent 能力的模块化封装:

Skill = 触发条件 + 执行逻辑 + 输出格式
  • 每个 Skill 是一个独立的能力单元(如"搜索网页"、"读取文件"、"运行测试")
  • Agent 根据用户意图动态加载合适的 Skill
  • 解决了"所有能力塞进 System Prompt 导致上下文爆炸"的问题

面试回答模板

最新的技术趋势是把 Agent 做成工程化的生产系统。Harness 是多 Agent 的运行时底座,负责编排、记忆、成本、安全。OpenClaw 是 Coding Agent 的执行框架,通过工具调用操作代码库。Skills 是能力的模块化封装,按需加载而非全量注入 System Prompt。

7. OpenClaw 如何设置 Memory?

Memory 分层设计

层级存储内容生命周期存储位置
短期记忆最近几轮对话、当前任务状态会话内上下文窗口
长期记忆用户偏好、项目结构、历史决策跨会话文件系统(如 CLAUDE.mdmemory/ 目录)
摘要记忆旧对话的压缩摘要跨会话文件系统

OpenClaw/Claude Code 的 Memory 实现

~/.claude/
├── settings.json          # 用户配置
├── projects/
│   └── {project-hash}/
│       └── memory/        # 长期记忆文件
│           ├── user_preferences.md
│           ├── project_context.md
│           └── MEMORY.md  # 记忆索引

设置方式

  1. 手动设置:用户说"记住 XXX",Agent 写入 memory 文件
  2. 自动设置:Agent 在交互中自动提取高价值信息写入 memory
  3. 读取方式:每次对话开始时自动加载 MEMORY.md 索引,按需读取相关记忆文件

面试回答模板

OpenClaw 的 Memory 分三层:短期记忆在上下文窗口中,保留最近几轮对话;长期记忆以文件形式存储在磁盘上,比如 CLAUDE.md 和 memory 目录;摘要记忆是旧对话的压缩版。每次对话开始时加载记忆索引,按需读取相关记忆文件。用户可以手动让 Agent 记住某些信息,Agent 也会自动提取高价值信息写入。

8. OpenClaw 没有向量数据库是如何查询的?

核心答案:基于文件系统 + Grep/Glob + LLM 理解

OpenClaw(Claude Code)不使用向量数据库,而是用以下方式定位信息:

方式说明
Grep(关键词搜索)用 ripgrep 在代码库中搜索关键词、函数名、变量名
Glob(文件模式匹配)用 glob 模式查找文件,如 **/*.pysrc/**/*.ts
Read(读取文件)直接读取文件内容,让 LLM 理解
目录结构探索ls 看目录结构,再逐层深入
LLM 推理根据已有信息推理"下一步应该看哪里"

为什么不需要向量数据库

场景向量数据库的优势OpenClaw 的替代方案
语义搜索理解"用户登录功能"能匹配到 auth.pyLLM 本身就理解语义,可以直接推理出应该看哪个文件
跨文件关联自动建立文件间语义关系Agent 通过多轮工具调用逐步建立关联
模糊查询"那个处理支付的函数"Grep 搜 paymentpaycharge 等关键词

关键洞察

向量数据库解决的是无 LLM 时的语义检索问题。当 Agent 本身就有一个强大的 LLM 作为"大脑"时,它可以自己推理"应该去哪里找什么",用 Grep/Glob/Read 就够了。LLM 本身就是最好的"语义搜索引擎"。

9. Cursor 中没有向量数据库,是如何定位到你要修改的位置?

Cursor 的定位机制

用户输入:"给登录函数加上错误处理"
   ↓
① LLM 分析意图 → 需要找到 auth/login 相关文件
   ↓
② 用 Grep 搜索关键词:login、auth、sign_in
   ↓
③ 用 Glob 搜索文件模式:**/*auth*、**/*login*
   ↓
④ 读取候选文件内容
   ↓
⑤ LLM 理解代码结构,确定具体函数位置
   ↓
⑥ 生成修改方案,调用 Edit 工具修改

运行时 Bash 文件内容

当 Cursor/Claude Code 执行 Bash 命令时,通常包含:

# 搜索文件
find . -name "*.py" | grep auth
# 或
grep -r "def login" --include="*.py" .

# 读取文件
cat src/auth/login.py

# 运行测试
python -m pytest tests/test_auth.py

# Git 操作
git diff
git add src/auth/login.py

核心答案

Cursor 不需要向量数据库,因为 LLM 本身就是语义理解引擎。它通过 Grep/Glob 快速定位候选文件,再通过 LLM 理解代码结构精确定位修改位置。这比向量数据库更灵活,因为 LLM 可以根据上下文动态调整搜索策略。

10. Linux 如何找文件?

常用命令

命令用途示例
find按条件递归搜索文件find /path -name "*.py" -type f
locate基于数据库的快速搜索locate "*.py"
grep搜索文件内容grep -r "function_name" /path
which查找可执行文件路径which python
whereis查找二进制、源码、手册whereis nginx
ls + 通配符列出匹配的文件ls /var/log/*.log

find 高级用法

# 按名称查找
find . -name "*.py"

# 按类型查找(f=文件,d=目录)
find . -type f -name "*.py"

# 按大小查找
find . -size +100M

# 按修改时间查找(7天内修改的)
find . -mtime -7

# 组合条件
find . -name "*.py" -mtime -7 -size +1k

# 执行操作(找到后删除)
find . -name "*.tmp" -exec rm {} \;

面试回答模板

Linux 找文件主要用 find 命令,支持按名称、类型、大小、时间等条件搜索。快速定位用 locate(基于数据库),搜索文件内容用 grep。生产环境中 find -exec 配合其他命令可以实现批量操作。

11. AI 生成后上传到云端,如何提高用户使用效率?

问题本质

AI 生成内容后需要上传到云端,如何让用户更快、更省地完成这个过程。

优化方案

层面优化策略说明
生成阶段流式输出(Streaming)用户不用等全部生成完,边生成边看到结果
生成阶段增量生成只生成变化部分,不重新生成全部内容
上传阶段增量上传只上传变化的文件/内容,不传全量
上传阶段压缩传输对文本/代码做 gzip 压缩后再上传
上传阶段并行上传多个文件并行上传,提升吞吐
上传阶段断点续传大文件支持分片上传,失败后从断点继续
部署阶段边缘部署就近接入 CDN/边缘节点,减少网络延迟
部署阶段预热缓存高频访问内容预加载到缓存
体验阶段预览功能上传前让用户预览/确认,减少无效上传
体验阶段后台上传上传过程不阻塞用户操作

部署优化(回答面试官可能想问的)

优化点说明
模型推理优化用 vLLM/TensorRT-LLM 加速推理,降低首 Token 延迟
模型量化INT8/INT4 量化减少显存占用,提升吞吐
批处理多个请求合并推理,提升 GPU 利用率
异步架构生成任务放入消息队列,异步处理,用户轮询/WebSocket 获取结果
CDN 加速静态资源走 CDN,API 走就近节点

面试回答模板

主要从三个层面优化:生成阶段用流式输出和增量生成,让用户不用等;上传阶段用增量上传、压缩传输、并行上传减少耗时;部署阶段用边缘节点、预热缓存降低延迟。如果是模型推理场景,还可以用 vLLM 加速推理、量化减少显存占用、异步架构避免阻塞用户。

总结:面试核心考点

考点涉及题目核心知识
RAG 工程Q1-Q3完整流程、切块策略、指代消解
Agent 架构Q4-Q6Handoff 设计、主流架构、最新技术趋势
AI Coding AgentQ7-Q9Memory 设计、无向量数据库的检索方案
工程基础Q10Linux 文件操作
系统优化Q11流式输出、增量上传、部署优化
© 版权声明
THE END
喜欢就支持一下吧
点赞 2 分享 收藏

评论 (0)

取消

Warning: file_put_contents(/var/www/html/usr/cache/pagecache/7e/7e5cf59ec31c6987e75cacd014a131cc.cache): failed to open stream: No such file or directory in /var/www/html/usr/plugins/PageCache/Plugin.php on line 188