1000道算法工程师面试题(大模型)—— 第16部分
本文探讨了LLM在代码生成、工具调用和智能体应用中的关键技术方案。重点包括:1)构建"生成-测试-修复"循环的自动编程系统;2)通过Prompt优化、模型选择和后处理提升代码可执行率;3)采用Prompt防御、静态分析和沙箱隔离确保代码安全;4)工具调用系统的Schema设计和解析方法;5)多工具调用的顺序控制和依赖处理;6)通过硬限制和循环检测避免Agent无限循环;7)基于
·
这是关于 LLM 代码生成、工具调用(Function Calling)与 Agent 智能体 的进阶面试题集。这一领域是目前应用层最活跃的方向,考察重点在于工程落地的稳定性、安全性和可控性。
216. 你如何设计一个“代码生成 + 单元测试 + 修复循环”的自动编程系统?关键组件有哪些?
核心架构:Iterative Refinement Loop (迭代优化循环)。
关键组件:
- Generator (LLM Coder): 负责根据需求生成代码。
- Prompt 技巧: 要求输出完整的 Markdown 代码块,并包含必要的 import。
- Executor (Sandbox): 一个安全的隔离环境(如 Docker 容器),负责运行代码和测试用例。
- Judge (Test Suite):
- 单元测试生成器: 让 LLM 针对需求先写测试用例(TDD 模式)。
- 执行与断言: 运行测试,捕获
stdout,stderr和Exit Code。
- Reflector (Feedback Loop):
- 如果测试通过 -> 结束,输出代码。
- 如果测试失败 -> 将 报错信息(Traceback) + 源代码 + 原需求 再次喂给 LLM,提示:“测试失败,错误如下,请分析原因并修复代码。”
- Controller: 限制最大循环次数(如 5 次),防止死循环浪费 Token。
217. 对于代码生成任务,如何在 Prompt / 模型选择 / post-process 上做优化,提高可执行率?
- Prompt 层面:
- Chain-of-Thought (CoT): “请先写出伪代码或设计思路,然后再写具体实现。”
- FIM (Fill-In-the-Middle): 如果是补全任务,明确
<PRE>,<SUF>,<MID>标记。 - 依赖约束: 明确指定库版本(如
Use Pydantic v2, not v1)。
- 模型选择:
- 优先选择经过 Code SFT 的模型(如 DeepSeek-Coder-V2, Claude 3.5 Sonnet, Qwen2.5-Coder),它们对语法结构和 API 的记忆更准。
- 后处理 (Post-process) - 关键:
- AST 解析 (Static Analysis): 使用 Python
ast模块尝试解析生成的代码。如果解析失败(语法错误),直接在后处理阶段修复(如自动补全括号)或打回重生成,不进沙箱。 - Linter 自动修复: 调用
ruff或black进行格式化和简单的 import 修复。 - 代码提取: 稳健地提取 Markdown backticks 中的内容,过滤掉解释性文字。
- AST 解析 (Static Analysis): 使用 Python
218. 在真实工程项目中,如何限制 LLM 生成“危险代码”(如命令注入、越权读取等)?
三道防线 (Defense in Depth):
- Prompt 防御 (Pre-generation):
- System Prompt:
禁止使用 os.system, subprocess, exec, eval 等危险函数。
- System Prompt:
- 静态分析 (Post-generation):
- 在代码执行前,使用 AST 扫描代码树。
- 黑名单机制: 发现
import os,import shutil,open('/etc/passwd')等特征直接拦截。 - 正则扫描: 扫描 IP 地址、域名模式,防止外联。
- 沙箱隔离 (Runtime Execution):
- 网络隔离: 容器无外网权限(No Internet Access),防止数据外泄。
- 用户权限: 使用
nobody或非 root 用户运行代码。 - 文件系统: 只读挂载(Read-only FS),仅
/tmp可写。 - 资源限制: cgroups 限制 CPU 和 内存,防止挖矿或 Fork Bomb。
219. 设计一个“LLM + 工具调用(Tool Calling)”系统,你会如何描述工具 schema 并解析模型输出?
- Schema 定义 (Interface):
- 遵循 OpenAI Function Calling 格式 (JSON Schema)。这是目前的事实标准。
- 关键点:
description字段必须极其详尽。LLM 是靠 description 来语义匹配工具的。 - 示例: 不写
"get_weather(loc)",而写"get_weather(location: str): Get current temperature in Celsius for a given city name."
- 解析模型输出 (Parser):
- 容错解析: LLM 输出的 JSON 经常缺括号或带注释。使用
json_repair库或专门的“脏 JSON 解析器”。 - 流式解析: 许多框架支持在 Stream 过程中实时解析 function name 和 arguments,提升响应速度。
- 容错解析: LLM 输出的 JSON 经常缺括号或带注释。使用
- 验证 (Validation):
- 使用 Pydantic 对解析出的参数进行校验(类型强转、范围检查)。如果校验失败,将 ValidationError 自动反馈给 LLM 让其重试。
220. 多工具调用场景下,如何让模型按正确顺序调用工具,并处理前后依赖关系?
核心:ReAct (Reasoning + Acting) 或 Planning。
- 显式规划 (Explicit Planning):
- 在 System Prompt 中要求:
在调用工具前,先生成一个 Plan:1. 第一步做什么,2. 第二步做什么。 - 这能让模型意识到任务的依赖性(先查 ID,再查订单)。
- 在 System Prompt 中要求:
- 上下文传递 (Context Passing):
- 观察结果回填: Tool A 执行完,将结果(Output)作为
role="tool"的消息塞回 History。 - 自动依赖注入: 下一轮生成的 Prompt 包含了上一轮的结果。LLM 会自然地提取 Tool A 的输出作为 Tool B 的输入。
- 观察结果回填: Tool A 执行完,将结果(Output)作为
- DAG 执行器:
- 对于复杂的并行任务,让 LLM 生成一个 DAG(有向无环图)任务列表,代码层解析 DAG,并行执行无依赖的工具,串行执行有依赖的工具。
221. 你如何在 Agent 系统中控制“推理深度”和“调用次数”,避免无限循环和成本炸裂?
- 硬限制 (Hard Limits):
- Max Steps: 设定
max_iterations = 10。超过次数强制终止,返回“任务未完成”。 - Token Budget: 设定单次 Session 的最大 Token 消耗量。
- Max Steps: 设定
- 循环检测 (Loop Detection):
- 指纹匹配: 记录过去 N 步的
(Tool_Name, Tool_Args)。如果发现连续三次调用完全相同的参数,判定为卡死。 - 干预策略: 触发循环检测后,向 Context 中插入系统提示:
System: 你似乎陷入了循环调用,请尝试改变策略或结束任务。
- 指纹匹配: 记录过去 N 步的
- 明确终止条件:
- 提供一个
finish_task()或final_answer()工具,强迫模型在完成时显式调用,而不是一直空转。
- 提供一个
222. 在复杂业务流程(例如报销、工单审批)中,如何用 LLM + 工具调用做“流程编排”?
架构:SOP-based Agent (基于标准作业程序的智能体)。
- 状态机 (Finite State Machine - FSM):
- 不要把整个大流程都塞给 LLM。将流程拆分为状态:
提交 -> 初审 -> 复核 -> 打款。 - LLM 充当状态流转引擎。
- 不要把整个大流程都塞给 LLM。将流程拆分为状态:
- 动态工具挂载 (Dynamic Tool Binding):
- 在“初审”状态,LLM 只能看到
approve,reject,ask_for_more_info这三个工具。 - 只有状态流转到“打款”,才挂载
execute_payment工具。防止越权调用。
- 在“初审”状态,LLM 只能看到
- SOP 注入:
- System Prompt 中包含当前步骤的 SOP(标准操作流程):
当前是初审阶段,你需要核对发票金额,如果超过 5000 需转交人工。
- System Prompt 中包含当前步骤的 SOP(标准操作流程):
223. 你如何记录和回放 Agent 的“思考过程”和调用链,以便调试和优化?
可观测性方案 (Tracing):
- 结构化日志 (Trace/Span):
- 使用 OpenTelemetry 标准或 LangSmith/Langfuse 格式。
- Root Span: 用户 Query。
- Child Spans: Thought (LLM Generation) -> Action (Tool Call) -> Observation (Tool Output).
- 关键字段:
Step_Index,Latency,Token_Usage,Input_Context,Output_Result.
- 回放机制 (Replay):
- 将日志中的
Input_Context和Tool_Output提取出来。 - 在本地 Mock 掉工具调用(直接返回日志中的 Output),只让 LLM 重新推理。这样可以低成本验证“修改 Prompt 后,LLM 的推理逻辑是否变好了”。
- 将日志中的
224. 对于高可靠性场景,你会如何为 LLM 的工具调用增加“规则引擎”或“验证器”?
模式:Neuro-Symbolic AI (神经符号主义) - LLM 负责意图,代码负责规则。
- 参数校验器 (Pydantic/JSON Schema):
- 最基础的防御。确保转账金额是数字,日期格式正确。
- 业务规则层 (Guardrails):
- 在 LLM 生成 Tool Call 后,执行前 拦截。
- 规则示例: “如果
transfer_amount > 10000且user_level != VIP,拦截并报错。” - 实现: 类似于 NeMo Guardrails 或自定义 Python 装饰器。
- Human-in-the-loop (人工确认):
- 对于高危操作(删库、转账),规则引擎暂停执行,向前端推送“确认卡片”,用户点击 Approve 后,Agent 继续执行后续步骤。
225. 在一个多 Agent 协同系统中,你如何设计角色划分和通信机制?
常见模式:
- 角色划分 (Specialization):
- Router/Manager: 负责任务分发,不干具体活。
- Worker (Coder): 专注写代码。
- Worker (Reviewer): 专注找 Bug。
- Worker (Executor): 专注运行环境。
- 通信拓扑:
- 星型拓扑 (Supervisor): 所有 Worker 只和 Manager 说话。Manager 汇总信息后决定下一步叫谁。适合流程明确的任务。
- 总线拓扑 (Shared Blackboard): 所有 Agent 共享一个 Memory/History。Agent A 说句话,B 和 C 都能看见。适合头脑风暴。
- 状态图 (Graph-based - LangGraph): 明确定义节点(Agent)和边(转移条件)。
Coder --commit--> Reviewer --pass--> Merge。这是目前最可控的方案。
226. 你如何评估一个“Agent 系统”相对于简单 LLM 接口是否真的带来了业务价值?
评估维度:Actionability (行动力) & Success Rate (成事率)。
- 对比基准: 纯 Chat LLM 只能给“建议”。Agent 能“交付结果”。
- 指标: 任务闭环率 (Task Completion Rate)。用户说“帮我订票”,纯 LLM 只能给链接,Agent 能把票号返回。
- 成本/收益分析:
- Agent 通常慢(多轮交互)且贵(Token 多)。
- 公式:
Value = (Agent 节省的人工工时 * 时薪) - (Agent Token 成本 + 延迟带来的体验折损)。
- 鲁棒性 (Robustness):
- 测试 Agent 在工具报错、网络超时情况下的自愈能力。如果一报错就挂,还不如让人工来做。
227. 对于代码解释、调试类 Agent,你会如何让它安全地执行用户代码并限制资源使用?
Sandbox 实现细节:
- Ephemeral Containers (瞬时容器):
- 每个 Session 启动一个轻量级 Docker 容器或 Firecracker MicroVM。会话结束立即销毁。
- 资源配额 (Quotas):
- Time:
timeout=30s,防止死循环。 - Memory:
mem_limit=512MB,防止 OOM 攻击。 - Disk: 限制写入量,防止填满磁盘。
- Time:
- Kernel Level Isolation:
- 使用
gVisor或Kata Containers,给容器加一层内核隔离,防止容器逃逸(Container Escape)。
- 使用
228. 当 LLM 在工具调用返回错误时,你会如何提示模型进行自我纠错或切换备选方案?
策略:Error as Feedback (将错误视为反馈)。
- Raw Error Feed:
- 不要在代码层 Swallow(吞掉)异常。
- 将
Exception: File not found包装成ToolOutput返回给 LLM。
- Prompt 引导:
- System Prompt 中加入:
如果工具调用失败,请仔细阅读错误信息,尝试修改参数重试,或者尝试使用另一个工具。
- System Prompt 中加入:
- 重试策略 (Backoff):
- 如果是网络波动(503),代码层自动重试。
- 如果是参数错误(400),必须把球踢回给 LLM,让它修改参数。
229. 你如何设计一个“可观测性”体系来监控 Agent 的表现(成功率、调用次数、卡死率等)?
Dashboard 指标设计:
- 宏观业务指标:
- Pass Rate: 最终任务成功的比例。
- Avg Steps: 平均多少步完成任务(步数越少越智能)。
- 微观性能指标:
- Tool Error Rate: 哪个工具经常报错?(说明工具描述写得烂,或者工具本身不稳定)。
- Loop Rate: 触发死循环检测的频率。
- Hallucination Rate: 尝试调用不存在的工具的频率。
- 成本指标:
- Tokens per Task: 完成一个任务平均烧多少钱。
230. 如果一个 Agent 系统在生产中经常出现“奇怪行为”,你会从 Prompt、工具、环境哪个维度率先优化?
优化优先级:Prompt > Tools > Environment > Model。
- 第一顺位:Prompt (System Instructions):
- 原因: 成本最低,见效最快。
- 动作: 90% 的“奇怪行为”是因为指令模糊。通过增加 Few-shot Examples(少样本示例)明确告诉 Agent 在特定场景下该怎么做。
- 第二顺位:Tools (Description & Interface):
- 原因: 模型经常选错工具或填错参数。
- 动作: 优化 JSON Schema 的
description,把参数的边界条件写清楚。将一个复杂的万能工具拆分成三个原子工具。
- 第三顺位:Environment (RAG/Context):
- 原因: 上下文中有噪音干扰了推理。
- 动作: 优化 RAG 检索质量,减少 Context 长度。
- 最后顺位:Model (Fine-tuning):
- 原因: 只有当基座模型智商不够理解复杂逻辑时,才考虑微调。这是最重、最贵的手段。
更多推荐


所有评论(0)