百度智能云 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 TopKBM25 TopKVector + BM25Vector + 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_idsection_pathblock_typeversioneffective_timesource_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面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论

相关推荐

昨天 12:00
桂林学院 Java
我的找实习之路,得从大二下学期开始说起。当时我一个好哥们不打算考研,一心准备找工作,天天闷头做项目、攒经验。那时候我还特别不理解。结果到了大三上,身边所有人都开始各忙各的,有人备考保研、有人准备求职,我一下子慌了。反观自己,绩点平平,保研基本没希望,找工作更是啥也不会、毫无头绪,彻底陷入了迷茫。不想一直浑浑噩噩耗下去,大三开学之后,我就不再躺平内耗了。天天泡在图书馆,花了两个月死磕后端知识,跟着某马一步步敲完了苍穹外卖这个项目。本来满心欢喜,觉得自己总算有项目经验了,兴冲冲出去投简历,结果因为项目太大众化、太普通,简历投出去基本石沉大海,根本没有公司理我。看着身边同学早就打好了基础、手握不少面试机会,我第一次开始自我怀疑。为了丰富简历、给自己加点竞争力,我又花了一个月多吃透Redis,还额外做了黑马点评项目补充到简历上。一切准备妥当,我正式开始在boss上投递简历,等待面试机会。十一月底,靠着985学校的学历光环,我侥幸拿到了番茄小说的面试邀请。但我心里特别清楚自己底子薄弱、基础不扎实,根本没底气面试,于是硬着头皮把面试时间推迟到十二月底。接下来整整一个月,我就打磨项目细节、优化简历、刷面试八股、看面经、刷LeetCode。可真到面试那天,我超级紧张,说话结结巴巴,脑子直接宕机一片空白,毫无悬念地面试挂了。面试失利加上期末九门考试压身,那段日子真的特别难熬。熬完崩溃的期末周,我浑浑噩噩收拾行李回了家过年,整个人状态特别低迷。寒假的时候,一个同为老乡的哥们看我状态不好,跟我分享了很多面试经验和技巧,帮我慢慢找回了一点信心。过完年,我意外拿到了京东的面试机会,但因为期末备考搁置了学习,放假太久手感全没,八股知识也忘得差不多了,一面面了30分钟就结束了。接连不断的面试失败,让我特别焦虑、特别挫败。沉下心反思之后,我决定好好打磨项目,甚至专门花10刀开了Trae会员优化项目。二月初暑期实习陆陆续续开了,我抱着试试看、随缘一试的心态,投了腾讯的提前批。没想到几天后就收到了CSIG的面试邀请。这次面试在家进行,没有学校那么紧绷,我状态放松了很多,意外顺利通过了一面。但人就是容易飘,顺利过完一面我就有点轻敌了,结果二面翻车得彻彻底底。面试官深挖底层原理和场景设计题,我根本没接触过,脑子再次空白,30分钟流程就终止了。不过也不算白忙活,连续几次面试下来,我积累了不少经验,心态也比之前沉稳从容了很多。三月返校之后,我一边上课,一边不停海投简历,参加笔试。每天泡在牛客和公众号里看面经,蹲各种面试机会,不敢有一点松懈。没多久,我拿到了腾讯PCG的面试邀约。面试官特别好,氛围特别轻松,我发挥得很稳定,顺利拿下一面。可腾讯的招聘流程巨慢无比,我足足等了十一天,才等到二面。这场二面持续了两个小时,全程高强度拷打,难度拉满,我咬着牙硬撑,好不容易顺利通过。又煎熬地等了一周,迎来总监三面,三面主要考察业务场景设计,面试官很随和,没有刻意刁难。那天刚好是清明前一天,一切都特别顺利,我当时满心欢喜,觉得offer基本稳了,就天真地停止了所有简历投递,打算坐等上岸。结果清明假期结束回来,后台状态直接变灰,招聘流程终止,大概率是横向对比候选人被淘汰了。大半个月的努力全部白费,心里说不出的失落。除此之外,那段时间面字节、快手等,全都因为一些细节没做好接连落败。接二连三的打击直接把我打入谷底,那段时间我真的快要放弃找实习了。到了四月初,折腾了大半年,兜兜转转好像又回到了原点。消沉了一段时间后,我逼着自己调整心态、从头再来,开启新一轮大规模海投。期间投了腾讯TEG部门,结果只面了二十分钟就草草结束,直接被刷。这次敷衍又仓促的面试,让我心态彻底崩了,甚至之后看到腾讯的面试邀约都自带阴影,特别恐惧。但就在我最低谷的时候,转机突然来了。大家都说极少捞简历的TME,意外捞起了我的简历。这次面试我的状态出奇的好,发挥超常。一面结束之后,面试官知道我可以随时到岗,直接让我原地等待,几分钟之后无缝衔接二面。这时手感火热,我顺利拿下二面。之后周五的三面也侥幸通过,这周顺利完成HR面和背调,成功进入录用评估阶段。希望可以offer....------------timeline--------------4.12    简历投递4.14    一面+二面4.18    三面4.22    HR面+背调4.24    录用评估------------------------------------千言万语,最后只剩一句:不过些许风霜罢了。
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务