阿里 AI 应用开发 暑期一面秒挂
1. 自我介绍
2. 项目里怎么做意图识别,如何理解用户真正要解决的问题
意图识别不是简单做文本分类,而是把用户输入转换成系统能执行的任务。比如用户问“这个供应商最近还能不能继续合作”,表面上是一个问答,实际上可能需要查询供应商资质、历史履约、黑名单、逾期付款、合同违约记录和采购金额变化。这里要识别的不只是意图标签,还包括业务对象、时间范围、风险类型和是否需要调用工具。
工程上我会分成三层。第一层是规则和实体识别,先提取合同编号、供应商名称、采购单号、金额、日期这类强结构化槽位。第二层是模型意图分类,判断是合同审查、条款解释、供应商风险、付款异常还是流程咨询。第三层是置信度和兜底,如果置信度低,就让用户补充信息,而不是直接执行错误链路。
public class IntentResult {
private String intent;
private Map<String, Object> slots;
private double confidence;
private boolean needTool;
private boolean needClarify;
}
public IntentResult route(UserQuery query) {
Map<String, Object> slots = slotExtractor.extract(query.getText());
if (slots.containsKey("contractNo")) {
return new IntentResult("contract_audit", slots, 0.95, true, false);
}
IntentResult modelResult = llmIntentClassifier.classify(query.getText(), slots);
if (modelResult.getConfidence() < 0.65) {
modelResult.setNeedClarify(true);
}
return modelResult;
}
3. MCP 和 function call 的区别是什么,为什么生产里不能只靠 function call
function call 更像是模型厂商提供的一种工具调用能力,模型根据函数描述生成函数名和参数,然后业务侧执行函数。它解决的是“模型如何结构化调用工具”的问题,但它通常绑定在某个模型 API 形态里,工具生命周期、权限、观测、复用和跨模型治理能力有限。
MCP 更像是一套工具上下文协议,重点是把工具、资源、提示词、输入输出 schema、权限边界和连接方式标准化。它让工具不再只是某个 prompt 里的函数列表,而是可以被多个客户端、多个模型、多个 Agent 复用的能力。生产系统里如果只有 function call,很容易变成每个业务自己拼工具描述,参数不稳定,权限难治理,调用日志也不统一。
{
"tool": "query_supplier_risk",
"description": "查询供应商风险信息",
"input_schema": {
"type": "object",
"properties": {
"supplierId": { "type": "string" },
"riskTypes": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["supplierId"]
},
"timeout_ms": 3000,
"auth_scope": "SUPPLIER_RISK_READ"
}
4. 使用 MCP 时遇到过哪些问题,怎么解决
最常见的问题是工具描述不清晰,模型不知道什么时候该调用;输入 schema 太宽,导致参数随意生成;工具返回结果太长,挤占上下文;工具失败语义不明确,模型会把失败当成无数据;还有权限没下沉到工具层,导致模型虽然不能回答,但工具已经查出了敏感数据。
解决方式是工具设计时就强约束。工具名要表达动作和领域,参数必须结构化,枚举值尽量固定,返回结果要做摘要和裁剪。每个工具要有统一的错误码,比如 TIMEOUT、PERMISSION_DENIED、INVALID_ARGUMENT、NO_DATA、DOWNSTREAM_ERROR。模型拿到工具失败结果后,只能基于失败语义降级,不能补充不存在的信息。
public enum ToolErrorCode {
TIMEOUT,
PERMISSION_DENIED,
INVALID_ARGUMENT,
NO_DATA,
DOWNSTREAM_ERROR
}
public class ToolResponse<T> {
private boolean success;
private ToolErrorCode errorCode;
private String message;
private T data;
private boolean partial;
}
5. MCP 底层通常基于什么协议通信,为什么协议层要和业务工具解耦
MCP 底层可以基于 stdio、HTTP、SSE、WebSocket 等方式通信。stdio 适合本地工具或 IDE 插件场景,HTTP 适合服务端工具调用,SSE 适合服务端持续推送工具状态,WebSocket 适合双向交互。协议本身不是重点,重点是 MCP 把工具能力抽象成标准资源和调用接口,让上层 Agent 不需要关心工具到底是本地进程、远程 HTTP 服务,还是内部 RPC。
协议层和业务工具必须解耦。否则每接一个工具都要改 Agent 编排层,工具也不能复用。解耦后,Agent 只面向统一 schema、统一鉴权、统一超时、统一日志,底层工具可以由不同团队维护。这样在大厂环境里更重要,因为供应商、合同、财务、审批系统可能都属于不同域。
Agent Runtime
-> MCP Client
-> Tool Registry
-> Contract Tool Server
-> Supplier Risk Tool Server
-> Payment Tool Server
-> Approval Tool Server
6. Agent 推理逻辑从用户输入到工具调用再到 RAG,整个流程是不是让大模型自由发挥
生产环境不会让大模型完全自由发挥。更合理的方式是“确定性编排 + 模型局部决策”。比如输入进来后,先由规则和模型共同完成意图识别;再由编排器根据意图决定候选工具和检索范围;模型可以在候选工具里选择,但不能调用未授权工具;工具结果回来后,模型只能基于证据生成结论。
如果完全让模型自由规划,很容易出现工具乱调、重复调用、遗漏关键步骤、死循环和越权。尤其在合同、采购、财务这类场景,Agent 的推理链路必须可控。我的做法是把 Agent workflow 做成状态机,每一步有明确输入、输出、超时、重试和终止条件。
public enum AgentState {
RECEIVE_QUERY,
INTENT_RECOGNIZED,
RETRIEVE_CONTEXT,
CALL_TOOLS,
GENERATE_ANSWER,
VERIFY_ANSWER,
FINISHED,
FAILED
}
7. skills 解决的是什么问题,为什么不把所有工具都直接暴露给 Agent
skills 解决的是能力封装问题。工具通常是原子能力,比如查供应商、查合同、查付款、查审批记录;skill 是一个更高层的业务能力,比如“合同风险审查”“供应商准入判断”“付款异常归因”。一个 skill 内部可以编排多个工具、多个 RAG 检索和多个校验步骤。
如果把所有工具直接暴露给 Agent,模型面对几十个甚至上百个工具时,选择会不稳定,而且业务流程无法沉淀。skill 把复杂流程封装起来,对外提供清晰输入输出,对内管理工具调用顺序、异常处理、权限校验和结果合并。这样模型只需要选择 skill,而不是每次都临时规划底层工具链。
{
"skill": "contract_risk_audit",
"input": {
"contractId": "C20260425001",
"auditLevel": "STRICT"
},
"steps": [
"parse_contract_clause",
"retrieve_policy_rule",
"query_supplier_risk",
"query_payment_history",
"generate_risk_report"
]
}
8. 如何判断一个能力是否需要封装成 skill
判断标准不是“这个功能有没有用”,而是它是否有稳定的业务流程、是否会被反复调用、是否需要多个工具协同、是否有明确的输入输出、是否需要统一治理。比如“查询合同标题”没必要封装成 skill,它只是一个工具;但“合同付款风险审查”需要查条款、查历史付款、查供应商评级、查审批权限,就适合封装成 skill。
还有一个标准是错误成本。如果某个能力执行错了会带来高风险,就应该封装成 skill,在内部加校验、降级和审计,而不是让模型每次临时组织。skill 的本质是把高频、复杂、高风险的业务流程产品化。
public boolean shouldWrapAsSkill(Capability capability) {
return capability.isRepeated()
&& capability.hasStableWorkflow()
&& capability.needMultipleTools()
&& capability.hasClearInputOutput()
&& capability.needGovernance();
}
9. skills 的工具设计有哪些基本要求和注意点
skill 内的工具设计要做到小而稳定。每个工具只负责一个明确动作,输入参数尽量结构化,输出结果要可被模型消费。不要把一个工具设计成“万能查询”,否则模型不知道该传什么参数,返回也很难控制。工具还必须有幂等设计,尤其是涉及写操作、审批、通知、状态变更时,必须用业务唯一键防止重复执行。
另外,工具返回不能直接把数据库大字段全塞给模型。应该返回摘要、关键字段、证据引用和分页信息。工具层还要做权限校验、参数校验、超时控制、熔断、审计日志和脱敏。模型只是调用方,不应该承担安全边界。
public class SupplierRiskQuery {
@NotBlank
private String supplierId;
@NotEmpty
private List<String> riskTypes;
private LocalDate startDate;
private LocalDate endDate;
}
public class SupplierRiskResult {
private String supplierId;
private String riskLevel;
private List<String> evidence;
private boolean dataComplete;
}
10. skills 里的工具编排出现延迟放大,怎么优化
延迟放大通常来自串行调用太多、工具超时设置过长、下游慢接口没有隔离、RAG 检索和 rerank 过重、模型调用次数过多。优化时先看 trace,把 skill 内每一步耗时拆出来。能并行的工具不要串行,比如供应商资质、付款历史、审批记录可以并行查;强依赖步骤才串行。
还要做预算控制。一个 skill 要有总超时时间,每个工具有单独超时,不能一个慢工具拖死整个链路。对于非关键工具可以返回部分结果;对于高频数据可以加缓存;对于 RAG 可以先粗召回再精排,避免每次都全量 rerank。
CompletableFuture<SupplierRiskResult> riskFuture =
CompletableFuture.supplyAsync(() -> riskTool.query(supplierId), riskExecutor);
CompletableFuture<PaymentResult> paymentFuture =
CompletableFuture.supplyAsync(() -> paymentTool.query(supplierId), paymentExecutor);
CompletableFuture<ApprovalResult> approvalFuture =
CompletableFuture.supplyAsync(() -> approvalTool.query(supplierId), approvalExecutor);
CompletableFuture<Void> all = CompletableFuture.allOf(riskFuture, paymentFuture, approvalFuture)
.orTimeout(2500, TimeUnit.MILLISECONDS);
11. skills 里怎么做流程管控、工具调用错误、容错降级和死循环防护
流程管控要把 skill 做成有状态的 DAG 或状态机,而不是让模型一轮一轮自由调用。每个节点定义输入、输出、依赖、最大重试次数、超时、是否关键节点和失败策略。非关键节点失败可以降级,关键节点失败要终止并给出明确原因
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.