百度智能云 Agent 开发 暑期一面
1. 自我介绍
2. 前期用 Milvus,后期切到 FAISS,可能是什么原因?
答案:Milvus 更适合服务化、分布式、多租户、海量向量管理场景,提供索引管理、持久化、过滤查询、集群扩展和运维能力;FAISS 更偏本地高性能向量检索库,轻量、可控、部署简单,适合数据规模没有大到必须上分布式、但对延迟和工程可控性要求比较高的场景。
如果项目早期用 Milvus,可能是为了快速验证向量库能力,支持元数据过滤和服务化接口;后期切到 FAISS,可能是因为数据量可控、线上环境更希望减少外部组件依赖,或者 Milvus 的运维成本、资源占用、冷启动、版本兼容不符合需求。FAISS 的优势是本地化部署简单、延迟更可控、便于和业务进程绑定,但缺点是需要自己处理持久化、分片、增量更新和元数据过滤。
import faiss
import numpy as np
dim = 768
xb = np.random.random((10000, dim)).astype("float32")
xq = np.random.random((5, dim)).astype("float32")
index = faiss.IndexFlatIP(dim)
faiss.normalize_L2(xb)
faiss.normalize_L2(xq)
index.add(xb)
scores, ids = index.search(xq, 5)
print(ids)
3. 向量检索和混合检索的对比实验应该怎么设计?
答案:不能只看某几个 demo 问题的效果,要构造离线评测集。评测集里至少包含精确实体类问题、语义泛化类问题、多跳推理类问题、长文档定位类问题、同义表达类问题和噪声输入类问题。向量检索适合语义相似问题,但对订单号、规则编号、接口名、错误码、配置键这种精确 token 不稳定;BM25 或关键词检索在精确匹配上更稳;混合检索的目标就是让两者互补。
设计时可以对比 Vector TopK、BM25 TopK、Vector + BM25、Vector + BM25 + Rerank。指标不能只看最终回答,还要看召回本身,比如 Recall@K、MRR、nDCG、Top1 命中率,以及最终生成答案的引用准确率。真正工程里,混合检索通常不是简单拼接,而是召回、融合、重排、上下文组装一起优化。
def recall_at_k(results, gold_ids, k):
hit = 0
for res, gold in zip(results, gold_ids):
if any(doc in set(gold) for doc in res[:k]):
hit += 1
return hit / len(results)
4. 如果同一份知识同时有标题、正文、表格和附件,索引应该怎么建?
答案:不能把所有内容压成一个 chunk 直接向量化。标题代表层级语义,正文代表主要说明,表格代表结构化事实,附件可能是补充证据。更好的方式是分类型索引:标题路径做元数据,正文做语义向量,表格转成行级或单元格上下文,附件单独抽取并和主文档建立关联。检索时根据 query 类型决定召回通道。
比如用户问“退款时效是多少”,表格里的规则行可能比正文更关键;用户问“这个规则适用于什么场景”,标题层级和段落解释更关键。索引时要保留 doc_id、section_path、block_type、version、effective_time、source_url,否则后面做版本过滤和证据引用会很困难。
chunk = {
"doc_id": "policy_2026_05",
"section_path": ["售后规则", "退款", "时效"],
"block_type": "table_row",
"content": "签收后7天内可申请无理由退款,特殊类目除外",
"version": "v3",
"effective_time": "2026-05-01"
}
5. 怎么给 Agent 设计任务状态,而不是每轮都重新从对话里猜?
答案:Agent 如果每轮都从历史对话里重新猜状态,很容易丢约束、重复调用工具或者把已经否定的路径又走一遍。更合理的方式是维护一个结构化任务状态,把用户目标、当前阶段、已完成动作、已获得证据、失败原因、待确认问题和下一步候选动作显式记录下来。
状态应该是可压缩、可恢复、可审计的。对话历史只是原始输入,不能直接当成执行状态。比如工单处理 Agent 里,状态要记录当前订单、用户诉求、物流状态、规则命中、已调用工具、是否需要人工确认。这样即使中途断线,也可以根据状态继续,而不是重新生成一遍计划。
state = {
"goal": "判断用户是否满足退款条件",
"stage": "collect_evidence",
"entities": {"order_id": "O20260425001"},
"done_actions": ["query_order", "query_logistics"],
"evidence": ["物流显示未签收", "商品类目支持拦截退款"],
"pending": ["确认是否已发起仓库拦截"],
"next_actions": ["query_warehouse_status"]
}
6. Agent 的中断控制应该怎么做?
答案:中断控制不能只靠用户发一句“停一下”。系统层面要支持取消信号、任务状态持久化、工具调用可取消、未完成动作回收和部分结果保留。Agent 运行时最好有一个 run_id,每一步工具调用都绑定这个 run_id,当用户中断时,调度器把该任务标记为 cancelled,后续 action 不再继续执行,已经返回的 observation 可以进入历史状态。
难点是外部工具不一定真正支持取消。比如 HTTP 请求已经发出,数据库查询已经执行,模型推理已经开始,这时只能做逻辑取消,也就是结果回来后丢弃。对于支付、退款、发券这种有副作用的工具,还必须做幂等和二次确认,不能因为中断导致半执行状态。
class AgentRuntime:
def __init__(self):
self.cancelled = set()
def cancel(self, run_id):
self.cancelled.add(run_id)
def should_stop(self, run_id):
return run_id in self.cancelled
def step(self, run_id, action):
if self.should_stop(run_id):
return {"status": "cancelled"}
result = action()
if self.should_stop(run_id):
return {"status": "discarded"}
return {"status": "ok", "result": result}
7. 跨轮会话切分和连续任务交互怎么处理?
答案:跨轮任务的核心是区分“新任务”“旧任务补充信息”“旧任务纠正”和“旧任务中断”。不能只根据时间间隔判断,也不能完全依赖模型自由理解。工程上通常会做意图分类和状态匹配:如果新输入能补全当前 pending slot,就归入当前任务;如果显式切换目标,就开启新任务;如果否定了前面的条件,就回滚相关状态。
比如用户先说“帮我看这个订单能不能退款”,Agent 查到需要物流状态;用户接着说“刚才那个订单已经拒收了”,这就是对当前任务的补充,不应该新开会话。状态管理里要有任务栈或任务表,而不是只有一条聊天记录。
def route_turn(user_text, active_task):
if "不用了" in user_text or "停止" in user_text:
return "cancel_current"
if active_task and active_task.get("pending"):
return "continue_current"
if "帮我" in user_text or "查一下" in user_text:
return "new_task"
return "chat"
8. 调用大模型超时或异常时,Agent 应该怎么兜底?
答案:大模型调用异常要分类型处理。超时可以重试、降级到小模型、返回部分结果或转人工;限流可以排队、指数退避、切换备用供应商;内容安全拦截需要给出合规提示;模型返回格式错误可以做结构化修复或重新请求。最差的做法是直接把异常堆栈返回给用户。
对于 Agent 来说,模型异常还会影响状态一致性。比如计划已经生成但工具还没执行,和工具执行完但总结失败,是两种不同状态。系统要记录每一步的 checkpoint,失败后可以从最近的安全点恢复,而不是整个任务重跑。
import time
def call_with_retry(fn, retries=3, base_sleep=0.5):
last_err = None
for i in range(retries):
try:
return fn()
except TimeoutError as e:
last_err = e
time.sleep(base_sleep * (2 ** i))
return {"error": "model_timeout", "detail": str(last_err)}
9. 滑动窗口、摘要压缩、历史召回分别解决什么问题?
答案:滑动窗口解决的是最近上下文保真问题,把最近几轮完整对话保留下来,适合处理当前正在进行的细节。摘要压缩解决的是长历史占用上下文问题,把过去对话总结成结构化状态,保留结论和约束。历史召回解决的是长期记忆问题,把很久以前的相关信息通过检索找回来。
这三者不能互相替代。只用滑动窗口会遗忘长期信息;只用摘要会丢失原文细节;只用历史召回会受 embedding 召回质量影响。比较稳的做法是最近窗口 + 结构化摘要 + 可检索历史三层一起用。
context_pack = {
"recent_turns": ["最近3轮完整对话"],
"summary_state": "用户正在处理退款工单,已确认订单未签收",
"retrieved_memory": ["用户历史偏好:希望优先自动处理,不要频繁确认"]
}
10. 会话内容放数据库还是磁盘?怎么选?
答案:如果是线上多用户系统,会话内容一般放数据库或对象存储,不建议只放本地磁盘。本地磁盘简单,但扩容、迁移、容灾和多实例共享都很麻烦。数据库适合结构化状态、任务记录、消息索引和审计;对象存储适合保存大文件、完整日志、模型输入输出快照。
比较常见的设计是:会话元信息和状态放数据库,长文本和附件放对象存储,向量化后的历史片段放向量库,热数据进 Redis 做缓存。这样既能查询,也能控制成本。
CREATE TABLE agent_session ( session_id VARCHAR(64) PRIMARY KEY, user_id VARCHAR(64), status VARCHAR(32), state_json JSON, created_at TIMESTAMP, updated_at TIMESTAMP );
11. 对话历史为什么不能无脑全量保存到 Prompt?
答案:全量历史会带来三个问题:成本高、噪声大、冲突多。随着轮次增加,Prompt 里会混入大量已经过期的计划、失败的工具结果、用户否定过的假设和无关闲聊。模型看到这些内容时,不一定能准确判断哪些还有效,反而会把旧结论当成当前事实。
所以历史应该先分层处理。原始历史用于审计和回放,不一定进入 Prompt;结构化状态用于当前推理;高价值证据按需召回。进入 Prompt 的应该是“当前任务需要的信息”,不是“系统见过的所有信息”。
12. FastMCP 和 AgentScope 这类框架封装工具时,抽象层应该怎么设计?
答案:工具抽象的核心不是简单把函数暴露给模型,而是要定义清楚工具名称、参数 schema、权限、幂等性、副作用、超时、返回格式和错误语义。FastMCP 更偏协议化工具服务接入,适合把外部能力以统一接口暴露出来;AgentScope 更偏 Agent 编排和多智能体组织。真正项目里可以把工具分成协议层、适配层和业务层。
协议层负责和模型或 Agent runtime 通信;适配层负责参数校验、鉴权、超时、重试和日志;业务层才是真正调用订单、库存、搜索、数据库等服务。这样后续换框架时,不需要改业务逻辑,只改接入层。
from pydantic import BaseModel
class ToolInput(BaseModel):
order_id: str
reason: str | None = None
class ToolResult(BaseModel):
ok: bool
data: dict | None = None
error: str | None = None
def query_refund_policy(args: ToolInput) -> ToolResult:
return ToolResult(ok=True, data={"allow_refund": True})
13. 工具调用参数为什么必须做强校验?
答案:模型生成的工具参数不可信。它可能漏字段、编造字段、把字符串写成数组、把用户自然语言直接塞进 SQL 条件,甚至产生越权参数。工具层如果不做强校验,就可能造成错误查询、脏数据写入或者安全事故。
强校验至少包括类型校验、枚举值校验、权限校验、业务边界校验和副作用确认。比如退款工具不能只因为模型说“用户想退款”就执行,必须校验订单归属、状态、金额、规则、幂等键和人工确认标记。Agent 工程里,工具是安全边界,不是模型的附属函数。
def validate_refund(user_id, order):
if order["user_id"] != user_id:
raise PermissionError("订单不属于当前用户")
if order["status"] not in {"paid", "shipped"}:
raise V
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

查看8道真题和解析