上海极豆科技有限公司 AI-Agent 一面

1、简单讲一下,MCP 是什么?它和 function call 有什么区别?

MCP,一般指 Model Context Protocol,本质上可以把它理解成一种让大模型和外部工具、外部数据源、外部能力进行标准化连接的协议。它不是单纯某一个函数调用接口,而是想解决“大模型怎么以统一方式发现工具、读取资源、调用能力、获取上下文”这个问题。

如果只说直白一点,MCP 更像是给大模型接外部世界时定义了一套“通用插口”。比如一个 Agent 想访问数据库、文件系统、知识库、内部服务,如果每接一个系统都单独写一套协议,维护成本会很高。MCP 的思路就是把这些能力抽象成统一的协议层,让模型或 Agent 框架可以按同样方式去发现和使用它们。

而 function call 更像什么?更像是 LLM 输出一个结构化调用意图,然后由宿主程序去执行某个预先注册好的函数。重点在“函数调用”本身。也就是说,function call 主要关注的是:模型决定调用哪个函数、传什么参数、再把执行结果喂回模型。

两者的区别可以这么讲:

MCP 更偏协议层、生态层、标准化连接层;function call 更偏模型调用工具时的一种具体机制

所以 function call 更像一个“动作”,而 MCP 更像一套“规则和接口标准”。如果再说得更面试一点:

  • function call 解决的是“模型怎么调函数”
  • MCP 解决的是“模型和外部能力怎么标准化互通”

你可以把 function call 看成 MCP 体系里可能承载的一种能力形式,但 MCP 本身不等于 function call。

2、为什么会出现并行调用?保单查询、理赔查询不是应该先做意图判断吗?

正常情况下,确实很多系统会先做意图识别。比如先判断用户到底是在查保单、查理赔,还是查投保状态。如果意图非常明确,而且路由准确率高,那最省资源的方式当然是:先路由,再只调一个服务。

但实际项目里出现并行调用,往往有几个原因。

第一个原因是用户意图可能不干净。用户一句话里可能同时包含多个意图,比如:

“帮我看看这个保单现在状态怎么样,之前那次理赔有没有到账”

这时候就不是单一意图了,而是保单查询和理赔查询都相关。如果还坚持只走单路,很容易漏信息。

第二个原因是路由模型不是 100% 准。如果系统前面意图识别置信度不高,直接并发调用两个轻量查询服务,最后再聚合结果,有时候反而更稳。本质上是拿一点资源换召回和鲁棒性。

第三个原因是有些查询链路本身成本低、耗时短。比如查保单和查理赔底层都是读接口,而且响应时间差不多,那并发发出去再聚合结果,整体 RT 反而更短。

所以这题更成熟的回答不是“并行一定对”或者“先意图一定对”,而是:

是否并行,取决于意图识别的置信度、服务调用成本、用户问题是否可能多意图,以及整体延迟目标。

代码上,最常见就是 asyncio.gather

import asyncio

async def query_policy(user_id):
    await asyncio.sleep(0.2)
    return {"type": "policy", "data": "保单状态:生效中"}

async def query_claim(user_id):
    await asyncio.sleep(0.3)
    return {"type": "claim", "data": "理赔状态:审核通过"}

async def main():
    user_id = "u1001"
    policy_result, claim_result = await asyncio.gather(
        query_policy(user_id),
        query_claim(user_id)
    )
    print(policy_result)
    print(claim_result)

asyncio.run(main())

3、多用户也可以用 async gather 一起调吗?

可以,但这里要分清楚两个层面。asyncio.gather 本身可以把多个协程并发执行,所以从语义上讲,它当然可以同时处理多个用户请求里的多个异步任务。但这不等于“所有多用户请求都丢给一个 gather 就完事”。

如果是单个用户请求内部有多个 I/O 操作,比如查保单、查理赔、查用户画像,这时候 gather 很适合。如果是多个用户同时访问系统,通常是 Web 框架本身负责并发接入,比如 FastAPI、aiohttp、uvicorn 这些异步服务框架。每个请求进来后,在请求内部再决定要不要 gather 多个下游任务。

也就是说:

  • 多用户并发:主要靠服务框架、事件循环、协程调度
  • 单请求内多任务并发:常用 asyncio.gather

有一个很重要的注意点是,gather 适合 I/O 密集型任务,比如查库、调接口、读 Redis、访问远程服务。如果你拿它去跑纯 CPU 密集任务,比如大规模本地推理、复杂计算,就不一定有效,因为 Python 解释器和事件循环不是这么用的。这种情况一般要考虑线程池、进程池,或者直接把重任务下沉到独立服务。

另外,多用户并发时还要注意几个现实问题:数据库连接池够不够、Redis 连接数够不够、下游接口会不会被打挂、单个请求里的并发数要不要限流。所以答案不能只停留在“可以”。

代码

import asyncio

async def handle_one_user(uid):
    async def query_a():
        await asyncio.sleep(0.1)
        return f"{uid}-A"

    async def query_b():
        await asyncio.sleep(0.2)
        return f"{uid}-B"

    a, b = await asyncio.gather(query_a(), query_b())
    return {"uid": uid, "result": [a, b]}

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

AI-Agent面试实战专栏 文章被收录于专栏

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

全部评论

相关推荐

03-08 16:30
门头沟学院 Java
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

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