尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Agent系统设计七宗罪:从while循环到生产级自主决策体

Agent系统设计七宗罪:从while循环到生产级自主决策体 1. 这不是“加个while循环就叫Agent”——一个被严重低估的系统设计战场你有没有在凌晨三点改完第十七版prompt把LLM输出硬塞进JSON解析器结果又因为多了一个空格、少了一个逗号、或者返回了一段“好的我明白了”而崩溃你是不是也试过用while循环反复调用模型直到它“终于吐出正确格式”然后在日志里看到连续23次重试、平均响应延迟飙到8.7秒、错误率31%这不是你的问题——这是整个行业对“Agent”这个词最普遍、最危险的误读。标题里说的“别只会给LLM包一层while循环”根本不是在批评初学者而是在戳破一个广泛存在的认知泡沫把LLM当做一个可无限重试的黑盒API来调用本质上仍是单次推理思维和真正的Agent设计毫无关系。真正意义上的Agent是具备目标分解能力、工具调度意识、状态记忆机制、失败回溯逻辑、执行路径规划、容错降级策略和可观测反馈闭环的自主决策体。它不依赖“运气好一次就成”而是构建一套鲁棒的执行骨架让LLM在其中扮演“专家顾问”而非“全栈工人”。这篇文章要拆解的正是这七个无法绕开的设计取舍——它们不是技术选型清单而是你在画第一张架构图之前必须亲手写下的七道判断题。每个选择背后都直接决定你的Agent是能稳定跑通生产环境的业务组件还是只能在demo视频里闪亮5秒的玩具。我带团队落地过6个跨行业Agent项目从金融风控决策链到工业设备远程诊断助手踩过的坑足够填满三本笔记本。下面这七个取舍每一个都来自真实压测现场、客户投诉邮件和凌晨三点的紧急回滚操作记录。2. 七个核心设计取舍为什么选A而不是B从来不是技术问题2.1 取舍一目标驱动 vs. 指令驱动——你的Agent到底在“做什么”还是在“听什么”绝大多数初学者写的Agent本质是“指令驱动”的用户输入一句话Agent把它喂给LLMLLM吐出一段文字Agent再把文字包装成回复。整个流程像一个高级版的Echo服务。但真正的Agent必须是“目标驱动”的它接收的是一个可验证的终态目标例如“查出客户张三过去三个月所有逾期未还款项并生成催收优先级排序表”而不是一句模糊指令例如“帮我查查张三的账”。这个区别看似微小实则决定了整个系统的底层架构。目标驱动意味着Agent必须内置一套目标分解引擎。它需要把高层目标拆解为原子任务序列先调用CRM API查客户ID再用ID查账单流水过滤出逾期项计算逾期天数和金额权重最后调用报表生成服务。每一步都需定义输入约束、输出契约、失败阈值和替代路径。而指令驱动下LLM被迫承担全部分解工作——这不仅极大增加prompt复杂度更导致不可控的幻觉风险。我们曾在一个保险核保Agent中发现当用户说“看看这个保单能不能续保”LLM有17%概率自行虚构一个不存在的“核保规则库”并据此编造结论只因它没被明确告知“必须调用规则查询接口”。提示目标驱动的标志是——Agent内部存在一个显式的、可序列化的任务图Task Graph而非隐式的、依赖LLM自由发挥的文本流。这个图可以是静态预定义的如金融场景的SOP流程也可以是动态生成的如客服场景的意图树但绝不能缺失。实操中我们采用“目标-动作-约束”三元组建模法目标{ type: generate_collection_plan, customer_id: CUST-2024-8871, time_window: P3M }动作[ { tool: crm_search, params: { id: CUST-2024-8871 } }, { tool: billing_api, params: { customer_id: CUST-2024-8871, start_date: 2024-04-01 } } ]约束{ max_retries: 2, timeout_ms: 12000, fallback_tool: manual_review_queue }这种结构让调试变得极其清晰当任务失败时你能准确定位是CRM接口超时、还是账单API返回了非预期字段而不是在LLM的千字输出里大海捞针。更重要的是它天然支持审计——每个动作都有明确的输入输出日志满足金融、医疗等强监管行业的留痕要求。2.2 取舍二显式状态管理 vs. 隐式上下文拼接——你的Agent记得住自己做过什么吗“让LLM记住上下文”是另一个高危误区。很多方案把历史对话轮次、工具调用结果、中间变量一股脑塞进system prompt或context window指望LLM自己“理解”当前进展。问题在于LLM没有真正的记忆只有注意力权重它无法区分“用户刚说的”和“三轮前调用的API返回值”更无法在长上下文中精准定位关键状态变更点。我们在电商售后Agent压测中发现当对话超过12轮LLM对“已生成退货单号RD-2024-9912”的引用错误率高达43%常把它错记为“待审核”而非“已发货”。真正的状态管理必须是显式、结构化、可验证的。我们采用三层状态架构会话层Session State存储用户身份、会话ID、初始目标、全局超时设置。使用Redis哈希结构TTL设为24小时。执行层Execution State记录当前任务图进度、已完成动作、待执行动作、各动作的输入输出快照。采用JSON Schema严格校验每次状态更新都触发schema验证。记忆层Memory State长期记忆如用户偏好、历史决策模式存入向量数据库但仅用于增强检索不参与核心流程控制。关键设计点在于状态变更的原子性。每次工具调用后Agent必须执行三个不可分割的操作解析工具返回结果提取结构化字段如{status:success,tracking_number:SF123456789CN}更新执行层状态将shipping_task标记为completed存入tracking_number生成下一轮决策提示基于新状态而非原始上下文这个过程完全脱离LLM——它只负责根据当前状态生成下一步动作描述而状态更新由确定性代码完成。我们甚至在状态更新后加入校验钩子若检测到tracking_number字段为空但状态标记为completed立即触发告警并回滚。这种设计让Agent具备了传统软件系统的可靠性基因而不是AI模型的随机性。2.3 取舍三工具编排 vs. 工具调用——你的Agent是调度员还是搬运工把一堆API封装成函数然后让LLM“选一个调用”这只是工具调用Tool Calling而工具编排Tool Orchestration是让Agent理解工具间的依赖关系、数据流向、错误传播路径和资源约束。前者是LLM的附加能力后者是Agent的核心能力。举个典型反例一个招聘筛选Agent需要“解析简历PDF→提取技能关键词→匹配JD要求→生成评分报告”。如果只做工具调用LLM可能先调用PDF解析再调用JD匹配最后才想起要提取技能——但JD匹配工具根本无法处理原始PDF必须等待技能列表。这种顺序错误在真实场景中频繁发生导致大量无效调用和超时。我们的编排方案强制引入数据契约Data Contract每个工具声明其input_schema和output_schemaAgent运行时构建数据流图Data Flow Graph自动检测依赖环执行前进行静态分析若工具A输出skills: string[]而工具B输入要求skills: string[]则建立A→B边更关键的是错误传播控制。当PDF解析失败时传统方案会让LLM重新尝试或放弃而编排系统会检查该失败是否影响后续所有节点如果是则触发降级路径如启用OCR备用通道如果仅影响技能匹配则隔离该分支用通用模板生成报告。我们在物流追踪Agent中实现过三级降级L1主快递API失败 → 切换至聚合查询平台L2聚合平台无数据 → 启动用户位置反查基于发货地址估算L3所有外部源失效 → 返回预置的“运输中”状态预计时效区间这种编排能力无法靠LLM推理获得必须由确定性代码实现。它让Agent从“尽力而为”变成“可控交付”。2.4 取舍四确定性决策 vs. 概率性生成——你的Agent敢为自己的选择负责吗LLM输出本质上是概率分布采样这意味着同一输入可能产生不同输出。但在生产环境中你无法接受“今天生成催收话术A明天生成话术B后天生成法律风险提示”。Agent必须在LLM的不确定性之上构建确定性决策层。我们的方案是决策-执行分离架构决策层Deterministic基于规则引擎Drools或决策树scikit-learn导出的JSON生成动作指令Action Command格式严格为{tool:send_sms,params:{template_id:COLL-001,recipient:86138****1234}}执行层ProbabilisticLLM仅负责将动作指令渲染为用户可见内容User-Facing Content如短信文案“张三先生您好您尾号1234的信用卡已逾期15天请及时还款。【XX银行】”这个分离带来三个关键收益可审计性所有决策日志都是结构化JSON可直接入库分析无需NLP解析可测试性决策层可用单元测试覆盖100%分支执行层只需测试渲染模板可干预性当发现某类催收决策效果差可直接修改规则引擎无需重训LLM我们曾用此架构将金融催收Agent的合规审查通过率从72%提升至99.8%——因为所有决策都源于预审通过的规则集LLM只负责“润色”不参与“定性”。注意不要试图用temperature0消除LLM随机性。实测显示即使temperature0在长文本生成中仍存在token级波动。真正的确定性必须来自架构分层而非参数调节。2.5 取舍五同步执行 vs. 异步编排——你的Agent能处理“等一等”吗现实世界充满异步操作发邮件要等SMTP响应、调用第三方API可能超时、用户需要时间思考回复。但多数LLM Agent设计默认采用同步阻塞模型导致整个流程卡死。更糟的是有些方案用while循环“轮询”异步任务状态造成大量无效请求和资源浪费。我们采用事件驱动异步编排每个异步任务启动时生成唯一task_id存入Redis并设置TTLAgent立即返回{status:pending,task_id:TASK-2024-7788}独立Worker监听任务完成事件如Kafka消息更新Redis状态用户下次请求携带task_idAgent查询状态并决定继续或返回结果这套机制的关键创新在于状态机嵌套。一个顶层任务如“完成整套开户流程”包含多个子任务身份证OCR、人脸识别、征信查询每个子任务有自己的状态机。当人脸识别超时时系统不会中断整个开户而是标记该子任务为timeout并启动人工复核流程其他子任务继续运行。在证券开户Agent中这套机制将平均开户时长从23分钟降至8.4分钟失败率下降67%——因为不再需要用户全程等待也不再因单点故障导致全链路失败。2.6 取舍六轻量路由 vs. 重量推理——你的Agent需要多聪明才能做选择很多Agent框架把“工具选择”交给LLM完成认为“大模型懂一切”。但实际中90%的路由决策是高度结构化的根据用户意图类型intent、当前状态state、可用工具集合tools就能确定下一步。让LLM处理这种确定性逻辑既浪费算力又引入不必要风险。我们的双层路由机制L1 轻量路由5ms基于意图识别模型小型BERT微调 状态机规则快速匹配95%常见路径。例如当intentcheck_balance且statelogged_in直接路由至banking_api.get_balance。L2 重量推理~2s仅当L1无法匹配如遇到新意图、状态异常、工具冲突时才将上下文摘要送入LLM进行深度推理。这个设计让87%的请求绕过LLM显著降低延迟和成本。更重要的是它实现了路由可解释性运维人员能清晰看到“为什么选这个工具”而不是面对LLM的黑盒输出抓耳挠腮。我们在政务咨询Agent中统计过L1路由准确率达99.2%而L2推理的准确率仅83.7%——证明简单规则往往比复杂推理更可靠。实操心得L1路由的规则库必须支持热更新。我们用Lua脚本编写规则部署在Redis中修改后5秒内生效无需重启服务。这比训练新模型快三个数量级。2.7 取舍七可观测闭环 vs. 黑盒执行——你的Agent出了问题你能找到根因吗最后也是最致命的取舍是否构建完整的可观测闭环。没有日志、没有指标、没有链路追踪的Agent就像没有仪表盘的飞机——飞得再高一旦出事就是灾难。我们的可观测体系包含四个维度Trace链路追踪使用OpenTelemetry记录每个任务从接收目标到返回结果的完整路径标注LLM调用、工具执行、状态更新等关键节点Metric指标监控实时采集成功率、平均延迟、LLM token消耗、工具调用频次等27项核心指标Log结构化日志所有日志为JSON格式包含trace_id、session_id、task_id、step_name、duration_ms、error_code等字段Profile性能剖析对LLM调用进行token级分析识别prompt膨胀、冗余上下文、低效few-shot等性能瓶颈最关键的创新是根因自动关联。当某个任务失败时系统自动关联该任务的完整trace同一session的前序操作日志相同tool_id的最近10次调用指标趋势当前LLM实例的GPU显存占用率在一次支付失败排查中这套系统3分钟内定位到问题不是LLM出错而是支付网关在特定时段返回了非标准HTTP状态码499而我们的错误处理器未覆盖该码——这完全与LLM无关但传统方案会误判为“模型不稳定”。3. 实操落地从取舍到代码——一个可运行的Agent骨架3.1 架构全景图七个取舍如何落地为代码模块我们以一个简化的“智能会议纪要Agent”为例展示如何将前述七个取舍转化为可运行代码。该Agent需完成接收会议录音→转文字→识别关键决策项→生成待办事项→邮件发送给参会者。├── core/ # 核心引擎体现取舍1/2/4/6 │ ├── planner/ # 目标分解器取舍1目标驱动 │ │ └── task_graph.py # 生成任务图transcribe → extract_decisions → generate_actions → send_email │ ├── state/ # 状态管理器取舍2显式状态 │ │ ├── session.py # 会话状态Redis-backed │ │ └── execution.py # 执行状态JSON Schema validated │ ├── router/ # 双层路由取舍6轻量vs重量 │ │ ├── intent_classifier.py # L1意图识别小型BERT │ │ └── llm_router.py # L2深度推理仅fallback时调用 │ └── decision/ # 决策层取舍4确定性决策 │ └── rule_engine.py # Drools规则引擎配置 ├── tools/ # 工具编排取舍3工具编排 │ ├── speech_to_text.py # 转录工具声明input/output schema │ ├── nlp_extractor.py # 决策项提取依赖transcribe输出 │ └── email_sender.py # 邮件发送含降级SMTP失败→企业微信 ├── exec/ # 执行引擎取舍5异步编排 │ ├── async_worker.py # Kafka消费者处理异步任务完成事件 │ └── task_manager.py # 任务状态机支持pending/running/completed/failed └── observability/ # 可观测性取舍7可观测闭环 ├── tracer.py # OpenTelemetry trace注入 ├── metrics.py # Prometheus指标暴露 └── logger.py # 结构化JSON日志这个目录结构本身就是七个取舍的物理映射。注意core/目录下没有LLM调用代码——LLM只作为tools/中的一个可插拔组件存在其调用由decision/层触发输出由exec/层消费。这种解耦让每个模块可独立演进、测试和替换。3.2 关键代码片段状态管理与任务图生成以下是planner/task_graph.py的核心逻辑体现取舍1目标驱动和取舍2显式状态from typing import List, Dict, Any import jsonschema from jsonschema import validate # 任务图Schema定义取舍2状态可验证 TASK_GRAPH_SCHEMA { type: object, properties: { target: {type: string}, steps: { type: array, items: { type: object, properties: { id: {type: string}, tool: {type: string}, depends_on: {type: array, items: {type: string}}, input_schema: {type: object}, output_schema: {type: object}, max_retries: {type: integer, minimum: 0}, timeout_ms: {type: integer, minimum: 100} } } } } } def generate_task_graph(goal: Dict[str, Any]) - Dict[str, Any]: 根据目标生成任务图取舍1目标驱动 goal示例: {type: generate_meeting_minutes, recording_url: s3://bucket/meeting.mp3} if goal[type] generate_meeting_minutes: # 静态任务图SOP流程 graph { target: generate_meeting_minutes, steps: [ { id: transcribe, tool: speech_to_text, depends_on: [], input_schema: {recording_url: string}, output_schema: {text: string, duration_sec: number}, max_retries: 2, timeout_ms: 120000 }, { id: extract_decisions, tool: nlp_extractor, depends_on: [transcribe], input_schema: {meeting_text: string}, output_schema: {decisions: array, action_items: array}, max_retries: 1, timeout_ms: 30000 }, { id: send_email, tool: email_sender, depends_on: [extract_decisions], input_schema: {to_emails: array, content: string}, output_schema: {message_id: string}, max_retries: 3, timeout_ms: 60000 } ] } else: raise ValueError(fUnsupported goal type: {goal[type]}) # 强制Schema验证取舍2状态可验证 try: validate(instancegraph, schemaTASK_GRAPH_SCHEMA) except jsonschema.ValidationError as e: raise RuntimeError(fInvalid task graph: {e.message}) return graph这段代码的关键在于目标驱动generate_task_graph函数只接收结构化goal不处理原始文本显式依赖depends_on字段明确定义执行顺序避免LLM自由发挥契约先行每个step声明input_schema/output_schema为工具编排提供基础可验证性JSON Schema校验确保任务图永远符合预期结构3.3 状态更新与错误处理让Agent真正“记得住”和“扛得住”state/execution.py中的状态更新逻辑体现取舍2显式状态和取舍3工具编排import redis import json from datetime import datetime class ExecutionState: def __init__(self, session_id: str): self.redis redis.Redis() self.session_id session_id def update_step(self, step_id: str, status: str, input_data: dict None, output_data: dict None, error: str None) - bool: 原子性更新步骤状态取舍2状态变更原子性 # 1. 获取当前状态 key fstate:{self.session_id} current_state self.redis.hgetall(key) # 2. 构建新状态只更新指定step step_key fstep:{step_id} new_step_data { status: status, updated_at: datetime.now().isoformat(), input: json.dumps(input_data) if input_data else None, output: json.dumps(output_data) if output_data else None, error: error } # 3. 原子性写入Redis pipeline保证 pipe self.redis.pipeline() pipe.hset(key, step_key, json.dumps(new_step_data)) pipe.expire(key, 86400) # TTL 24h pipe.execute() # 4. 触发状态变更钩子取舍3错误传播控制 if status failed and error: self._handle_step_failure(step_id, error) return True def _handle_step_failure(self, step_id: str, error: str): 错误传播控制取舍3工具编排的错误处理 # 查询该step的依赖关系 task_graph self._get_task_graph() failed_step next((s for s in task_graph[steps] if s[id] step_id), None) if not failed_step: return # 根据错误类型和依赖关系决定降级策略 if step_id transcribe and timeout in error: # L1降级切换至备用ASR服务 self._trigger_fallback(transcribe_backup) elif step_id email_sender and smtp_error in error: # L2降级切换至企业微信 self._trigger_fallback(wechat_notifier) else: # L3降级标记为manual_review self.update_step(manual_review, pending, input_data{failed_step: step_id, error: error})这个实现确保状态更新不可分割Redis pipeline保证hset和expire原子执行错误即刻响应失败时立即触发降级而非等待LLM下一轮推理降级策略可配置不同工具、不同错误类型对应不同fallback路径3.4 可观测性集成让问题无所遁形observability/tracer.py中的链路追踪体现取舍7可观测闭环from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # 初始化Tracer生产环境连接Jaeger或Zipkin provider TracerProvider() processor BatchSpanProcessor(OTLPSpanExporter(endpointhttp://jaeger:4318/v1/traces)) provider.add_span_processor(processor) trace.set_tracer_provider(provider) def start_agent_trace(session_id: str, goal_type: str): 启动Agent全链路追踪取舍7可观测闭环 tracer trace.get_tracer(__name__) with tracer.start_as_current_span( agent_execution, attributes{ session_id: session_id, goal_type: goal_type, env: production } ) as span: # 记录关键事件 span.add_event(goal_received, {goal: str(goal_type)}) # 在每个核心步骤中创建子span with tracer.start_as_current_span(task_planning) as plan_span: task_graph generate_task_graph({type: goal_type}) plan_span.add_event(task_graph_generated, {step_count: len(task_graph[steps])}) with tracer.start_as_current_span(state_initialization) as state_span: state ExecutionState(session_id) state_span.add_event(state_initialized) # ... 其他步骤 ... span.add_event(execution_completed) return span.context.trace_id # 在工具调用中注入trace context def call_tool_with_trace(tool_name: str, params: dict, trace_id: str): tracer trace.get_tracer(__name__) with tracer.start_as_current_span( ftool_{tool_name}, attributes{tool_name: tool_name, params: str(params)} ) as span: # 注入trace_id到HTTP header如调用外部API headers {X-Trace-ID: trace_id} # ... 实际调用逻辑 ... span.add_event(tool_called, {status: success})这套追踪体系让每个Agent执行都生成一条完整链路包含顶层agent_executionspan标识会话和目标子span如task_planning、state_initialization标识各模块耗时工具调用span标识外部依赖性能关键事件如goal_received、execution_completed当出现延迟问题时运维人员可直接在Jaeger UI中查看火焰图精准定位是task_planning耗时过长需优化规则引擎还是tool_speech_to_text响应慢需联系ASR供应商。4. 常见问题与避坑指南那些没人告诉你的血泪教训4.1 “LLM返回JSON格式错误”——这不是LLM的问题是你的契约没定好网络热词中高频出现的“修复llm返回json的java库”暴露了一个根本性误解试图用后处理修复LLM的输出而不是在源头约束其行为。我们统计过83%的JSON解析失败源于LLM生成了非法字符如中文引号、未转义换行符或结构偏差如多出逗号、少括号而非语义错误。正确解法在LLM调用前用结构化Prompt JSON Schema约束你是一个严格的JSON生成器。请严格按照以下JSON Schema输出不得添加任何额外字段、注释或说明文字 { type: object, properties: { action_items: { type: array, items: { type: object, properties: { task: {type: string}, owner: {type: string}, due_date: {type: string, format: date} }, required: [task, owner, due_date] } } }, required: [action_items] }同时在代码层强制校验import jsonschema from jsonschema import validate def safe_json_parse(llm_output: str, schema: dict) - dict: try: data json.loads(llm_output) validate(instancedata, schemaschema) return data except json.JSONDecodeError as e: # 记录原始输出用于debug logger.error(fJSON decode error: {e}, raw_output: {llm_output[:200]}) raise ValueError(Invalid JSON format) except jsonschema.ValidationError as e: logger.error(fJSON schema validation error: {e.message}) raise ValueError(JSON does not match schema)实操心得永远不要信任LLM的JSON输出。我们曾因一个未捕获的json.JSONDecodeError导致整个批处理任务静默失败损失了27小时的数据同步。现在所有LLM JSON输出都经过双重校验先json.loads()再jsonschema.validate()任一失败都触发告警。4.2 “Agent执行terminated due to error”——错误处理不是写个try-catch就够了这个报错信息在Hermes Agent等框架中很常见根源往往是错误处理粒度太粗。把整个Agent流程包在一个try-catch里一旦出错就全链路终止完全违背了Agent应有的韧性。正确解法按任务图节点粒度处理错误def execute_task_graph(graph: dict, state: ExecutionState): for step in graph[steps]: try: # 执行step result call_tool(step[tool], step[input]) # 更新状态 state.update_step(step[id], completed, output_dataresult) except ToolTimeoutError as e: # 节点级超时处理 state.update_step(step[id], failed, errorftimeout: {str(e)}) if step.get(fallback_tool): # 启动fallback fallback_result call_tool(step[fallback_tool], step[input]) state.update_step(f{step[id]}_fallback, completed, output_datafallback_result) else: # 标记为需人工介入 state.update_step(manual_intervention, pending, input_data{step_id: step[id], error: str(e)}) except Exception as e: # 兜底错误处理 state.update_step(step[id], failed, errorfunknown: {str(e)})这种设计让Agent具备“局部失败全局存活”的能力。即使某个工具永久失效系统仍能通过fallback或人工介入继续推进。4.3 “Dify的SQL查询内容太多导致LLM返回不稳定”——不是LLM不行是你的数据没剪枝Dify等低代码平台常遇到此问题当SQL查询返回数百行数据LLM prompt迅速膨胀导致token超限、响应变慢、输出质量下降。这不是LLM缺陷而是数据管道设计失误。正确解法在LLM之前插入智能数据剪枝层def smart_prune_sql_result(sql_result: List[dict], max_rows: int 10) - List[dict]: 智能剪枝保留关键行 统计摘要取舍4确定性决策 if len(sql_result) max_rows: return sql_result # 1. 保留首尾各2行看开头和结尾 pruned sql_result[:2] sql_result[-2:] # 2. 添加统计摘要确定性生成非LLM summary { total_rows: len(sql_result), columns: list(sql_result[0].keys()) if sql_result else [], sample_values: {k: [r[k] for r in sql_result[:3]] for k in sql_result[0].keys() if sql_result} if sql_result else {} } # 3. 将摘要作为独立字段加入结果 pruned.append({__summary__: summary}) return pruned # 使用示例 raw_data execute_sql(SELECT * FROM orders WHERE statuspending) pruned_data smart_prune_sql_result(raw_data) # LLM prompt中只包含pruned_data而非原始大数据集这个剪枝层完全确定性不依赖LLM却解决了90%的大数据量问题。我们在电商Agent中应用后LLM响应延迟从平均4.2秒降至0.8秒token消耗减少76%。4.4 “Prompt injection attack to tool selection”——安全不是加个filter是重构信任边界NDSS 2026论文提到的prompt注入攻击本质是利用LLM对指令的盲目服从。传统方案用正则过滤敏感词但攻击者总能找到绕过方式如base64编码、Unicode混淆。正确解法实施零信任工具调用协议指令白名单LLM输出必须匹配预定义的动作模板否则拒绝执行# 预定义模板 TOOL_CALL_TEMPLATES { search_knowledge: r{tool: search_knowledge, params: {query: [^]}}, send_email: r{tool: send_email, params: {to: [^], subject: [^], body: [^]}} } def validate_tool_call(llm_output: str) - dict: for tool, pattern in TOOL_CALL_TEMPLATES.items(): if re.fullmatch(pattern, llm_output.strip()): return json.loads(llm_output.strip()) raise ValueError(Invalid tool call format)参数沙箱所有工具参数必须通过白名单校验def sanitize_email_params(params: dict)
返回列表