最优提问:为AI智能体上下文获取定义目标函数的工程实践

发布时间:2026/8/26 13:22:29
最优提问:为AI智能体上下文获取定义目标函数的工程实践 很多AI智能体应用上线后效果不好问题往往不在模型本身而在“上下文没拿对”。用户问了一句“这个月的营收为什么下滑”Agent 如果直接去知识库检索大概率会召回一堆不相关的财务公告如果先补充约束、拆分查询、明确时间范围和指标口径再去做检索和工具调用回答质量会明显提升。这个差距的本质是提问质量决定了上下文获取质量。传统数据库里有“查询优化器”会为一条 SQL 自动选择执行计划AI 智能体同样需要一个“提问优化器”把“如何提问”从拍脑袋变成可计算、可优化、可评估的工程问题。而这正是“最优提问为AI智能体上下文获取定义目标函数”要解决的。本文会讲清楚三件事第一为什么上下文获取是 Agent 应用的核心瓶颈第二怎么把“提问质量”定义成一个可计算的目标函数第三给出一套最小可运行的工程实现包括候选提问生成、目标函数打分、最优提问选择、接口 API 和批量任务设计。如果你在做 RAG、企业知识库问答、Agent 工具调用编排或者想把“自动提问”接入合规检测、客服质检等流程这篇文章可以直接收藏。1. 核心问题上下文获取为什么卡住 AI 智能体传统聊天机器人是“用户问一句模型答一句”上下文完全来自对话历史。AI 智能体不一样它需要主动获取上下文查知识库、调业务 API、搜索网页、追问用户、读取文件。上下文拿得越准最终答案越可靠。但在实际落地中上下文获取往往是最脆弱的一环。常见场景有三个。第一知识库检索。用户问题进入 RAG 流程后第一步是生成检索 query。如果 query 直接复述用户口语化问题召回结果经常会漂。用户说“服务器最近老出问题”系统不知道要查“哪个项目”“什么时间范围”“哪类故障”检索出来的文档自然五花八门。第二工具调用。Agent 需要决定调用哪个工具、传什么参数。工具参数就是提问的一部分。用户说“帮我看看杭州那边的情况”如果 Agent 不追问“杭州是哪个项目的杭州”“要看什么指标”就只能猜参数最后大概率调用失败或返回错误结果。第三多轮追问。高质量的上下文获取经常需要 Agent 主动追问。但追问也有成本用户不耐烦、多消耗一次模型调用、延长响应时间。追什么、追几条、追完怎么用都需要策略。这三个场景有一个共同点存在一个“提问动作”这个动作产生一个 query、一组参数、一条追问。提问动作的质量直接决定了后续获取到的上下文质量。关键问题就变成了能不能用一个目标函数给每个候选提问动作打分让系统自动选择“最优提问”可以而且这个思路可以工程化。2. 最优提问的目标函数定义框架目标函数不是玄学它就是一个可计算的评分函数。输入是一个候选提问动作输出是一个分数。系统生成多个候选提问挑分数最高的去执行。这里的“提问动作”可以是改写后的检索 query一次工具调用的参数组合一条向用户发起的追问一个包含多个子问题的查询计划。我给出一套通用的五维评分框架。你在实际项目中可以裁剪、加权、替换。Score(Q) w1 * Relevance(Q) w2 * InfoGain(Q) w3 * Executability(Q) w4 * CostPenalty(Q) w5 * Compliance(Q)维度含义计算方式示例Relevance语义相关性用 query 与候选文档/上下文向量的 top-k 平均相似度或检索命中分数InfoGain信息增益检索结果相比当前已有上下文的新增信息量可用新召回文档的差异度估算Executability可执行性工具参数是否完整、query 是否可被检索系统接受可由 LLM 打分或规则校验CostPenalty成本惩罚根据预估 token 消耗、API 调用次数、延迟折算为负向分数Compliance合规约束是否越权、是否包含敏感数据、是否符合权限范围违规直接置 0 或重罚这五个维度不需要全部可精确计算。工程上可以混合使用规则、向量计算和 LLM 打分。Relevance 最容易做因为大部分知识库已经接入了向量检索可以直接拿到相似度分数。InfoGain 可以用“检索结果集合里的新增文档数 / 总文档数”来估算新增比例越高说明这个提问带来的上下文增量越大。Executability 适合用 LLM 打分给它一个评分标准问它“这个 query 是否包含足够的信息用于调用指定工具”输出 0 到 1 的分值。CostPenalty 用预估 token 数做归一化例如CostPenalty(Q) min(1.0, estimatedTokens / 2000) * 0.1这个公式是一个通用模板实际参数需要按业务场景标定。Compliance 维度在企业场景尤其重要。如果 Agent 部署在合规检测系统里提问动作必须要检查权限边界不能把未脱敏的客户数据拼进 query不能检索越权知识库不能触发敏感信息外发。合规检查结果应该是一票否决制违规候选提问直接过滤掉而不是靠权重拉回来。3. 从“提问生成”到“提问选择”的完整链路目标函数定义好之后还要有一条完整的处理链路。推荐使用“生成-评估-选择-验证”的四段式结构。第一步候选提问生成。用 LLM 基于用户原始问题生成 N 个候选提问。生成时需要让模型做几件事补充缺失约束、拆分复杂问题、改写为更利于检索的 query、生成工具调用参数。第二步目标函数评估。对每个候选提问执行一次轻量评估跑向量检索拿相关性分数跑规则检查合规性用 LLM 打分器评可执行性统计预估 token 成本。这个阶段不执行真正的业务工具调用只做快速筛选。第三步最优提问选择。按加权总分排序选 top-1 或 top-k。top-k 的场景是一个问题需要多路召回多个子问题分别检索再汇总上下文给最终模型。第四步验证与反馈。用选出的最优提问真正去检索或调用工具拿到上下文后再让评分器判断“这些上下文是否足够回答原始问题”。如果不够可以进入第二轮追问或改写形成循环。这个链路可以用 LangGraph、Dify、Coze 这类 Agent 编排框架实现也可以自己写一个队列服务。下面我会给一个不依赖任何重量级框架的最小实现核心代码可以直接套用。4. 本地部署与环境准备这个方案不是某个具体开源仓库而是一套工程实践。所以环境准备按通用 AI 服务部署标准来。推荐环境项目建议操作系统Linux / Windows / macOS 均可生产建议 LinuxPython3.10 或更高LLM 推理本地可用 Ollama Qwen/LLaMA也可用兼容 OpenAI API 的云端模型向量库Chroma、Milvus、Qdrant或已有知识库系统Agent 编排LangGraph 可选不是必须GPU如果本地跑 LLM建议显存按模型要求确认7B 量化模型通常 6G 起13B/32B 需要更高显存以实际官方要求为准如果你用 Ollama 起本地模型流程非常简单ollama pull qwen2.5:7b ollama serve启动后本地会有一个兼容 OpenAI 格式的接口默认地址一般为http://127.0.0.1:11434/v1。后面的 Demo 代码会直接通过这个地址调用。如果显存有限可以把“生成候选提问”和“目标函数打分”拆开生成用大模型打分用规则加向量检索尽量少调用 LLM。这样 8G 显存左右的机器也能跑通全流程具体显存占用需要以本机实际模型为准。5. 工程实现一个最小可运行的最优提问服务下面给出一个完整的 Python Demo包含候选提问生成、目标函数打分、最优提问选择和最终回答。这个 Demo 故意不绑定具体向量库你可以把search_knowledge_base函数替换成自己的知识库检索实现。import os import re from typing import Dict, List from openai import OpenAI # 适配 Ollama 或任意 OpenAI 兼容接口 LLM_BASE_URL os.getenv(LLM_BASE_URL, http://127.0.0.1:11434/v1) LLM_API_KEY os.getenv(LLM_API_KEY, ollama) LLM_MODEL os.getenv(LLM_MODEL, qwen2.5:7b) client OpenAI(base_urlLLM_BASE_URL, api_keyLLM_API_KEY) def chat(messages: List[Dict], temperature: float 0.7) - str: response client.chat.completions.create( modelLLM_MODEL, messagesmessages, temperaturetemperature, ) return response.choices[0].message.content def generate_candidate_queries(question: str, business_hint: str ) - List[str]: 基于用户问题生成多个候选提问。 prompt f你是一个专业的问题优化器。用户原问题是{question} 请生成 4 个不同的候选提问用于知识库检索和工具调用。 要求 1. 候选提问必须比原问题更具体补充必要的约束条件。 2. 至少一个候选提问把复杂问题拆解成单一子问题。 3. 至少一个候选提问面向工具调用包含结构化参数描述。 4. 不要编造业务事实只做查询改写。 业务背景可选{business_hint} 输出格式每行一个候选提问直接输出不要编号。 result chat([{role: user, content: prompt}], temperature0.9) return [line.strip() for line in result.splitlines() if line.strip()] def search_knowledge_base(query: str, top_k: int 3) - List[Dict]: 替换为真实向量检索实现。这里演示返回模拟结果。 return [ {content: f与 {query} 相关的文档片段1, score: 0.85}, {content: f与 {query} 相关的文档片段2, score: 0.72}, ] def calculate_relevance(query: str) - float: 相关性分数用检索命中分数的均值估算。 hits search_knowledge_base(query, top_k3) if not hits: return 0.0 return sum(hit[score] for hit in hits) / len(hits) def calculate_executability(query: str) - float: 可执行性用 LLM 打分0 到 1。 prompt f请评估以下候选提问是否适合直接用于知识库检索或工具调用。 打分标准 - 0.9-1.0包含明确实体、时间和限定条件。 - 0.6-0.8大部分信息完整但缺少一个关键约束。 - 0.3-0.5信息模糊依赖系统猜测。 - 0-0.2完全无法执行。 只输出一个数字不要输出其他内容。 候选提问{query} try: score float(chat([{role: user, content: prompt}], temperature0.0).strip()) return max(0.0, min(1.0, score)) except Exception: return 0.5 def calculate_compliance(query: str, allowed_scope: List[str] None) - float: 合规检查越权实体、敏感 ID、涉密字段直接返回 0。 if allowed_scope is None: allowed_scope [public] sensitive_patterns [ r\b\d{17}[\dXx]\b, # 身份证号 rtoken[: ][\w-]{20,}, # 疑似 token ] for pattern in sensitive_patterns: if re.search(pattern, query): return 0.0 # 实际项目需要对接权限系统这里只做演示 return 1.0 def objective_score(query: str, weights: Dict[str, float]) - Dict: 目标函数综合打分。 relevance calculate_relevance(query) executability calculate_executability(query) compliance calculate_compliance(query) # 成本惩罚预估 token 越少越好这里粗略用 query 长度估算 estimated_tokens max(1, len(query) / 2) cost_penalty min(1.0, estimated_tokens / 2000) * 0.1 score ( weights.get(relevance, 0.4) * relevance weights.get(executability, 0.3) * executability weights.get(info_gain, 0.1) * relevance # InfoGain 可用相关性近似 weights.get(cost, 0.1) * (1.0 - cost_penalty) weights.get(compliance, 0.1) * compliance ) if compliance 0: score 0.0 return { query: query, score: score, relevance: relevance, executability: executability, compliance: compliance, cost_penalty: cost_penalty, } def select_best_query(question: str, business_hint: str , weights: Dict[str, float] None) - Dict: 生成候选提问并用目标函数选出最优提问。 if weights is None: weights { relevance: 0.4, executability: 0.3, info_gain: 0.1, cost: 0.1, compliance: 0.1, } candidates generate_candidate_queries(question, business_hint) scored [objective_score(q, weights) for q in candidates] scored.sort(keylambda x: x[score], reverseTrue) return scored[0], scored if __name__ __main__: question 这个月的营收为什么下滑 best, all_scores select_best_query( question, business_hint业务系统是电商平台营收口径为已支付订单金额 ) print(最优提问, best[query]) print(目标函数分数, best[score]) print(全部候选, all_scores)这个 Demo 做了三件事生成候选提问、计算目标函数分数、输出最优提问。你可以直接跑通然后把它替换成真实业务实现。5.1 目标函数的可配置化在实际项目中权重不能写死在代码里。建议把权重放到配置文件中方便不同业务线复用。{ optiquery: { candidate_num: 4, weights: { relevance: 0.4, executability: 0.3, info_gain: 0.1, cost: 0.1, compliance: 0.1 }, compliance: { allowed_scope: [public, internal], blocked_entities: [customer_private_data], enable_sensitive_pattern: true }, search: { top_k: 3, min_score: 0.6 } } }权重怎么标定推荐先跑一批历史问题记录“用原问题直接检索”和“用最优提问检索”的效果差异再根据差评场景调整权重。如果发现“检索结果相关但答案仍然不对”说明 relevance 权重过高应该提高 executability 或 info_gain如果发现“追问过多导致用户流失”说明 cost 惩罚太低。5.2 接入 LangGraph 的编排思路如果你的项目已经在用 LangGraph可以把上面的逻辑封装成节点。整体流程是用户输入节点接收原始问题。提问优化节点调用generate_candidate_queries。目标函数评估节点对每个候选执行objective_score。最优选择节点取 top-1 或 top-k。检索执行节点真正执行知识库检索或工具调用。答案生成节点把检索到的上下文交给 LLM 生成最终答案。质量反馈节点判断答案是否满足要求不满足则回到提问优化节点。这个链路的核心价值在于原来“改写 query”是一次盲试现在每次改写都有目标函数打分Agent 的行为可解释、可测试、可回归。6. 接口 API 与批量任务设计要让这个“最优提问”能力被业务系统使用最好封装成服务。这里用 FastAPI 做一个轻量服务示例。from fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional app FastAPI() class AskRequest(BaseModel): question: str business_hint: Optional[str] top_k: Optional[int] 1 class AskResponse(BaseModel): best_query: str score: float candidates: List[Dict] answer: Optional[str] app.post(/api/optiquery/ask, response_modelAskResponse) def ask(req: AskRequest): best, all_scores select_best_query(req.question, req.business_hint) # 这里用最优提问去检索上下文再由 LLM 生成最终答案 answer return AskResponse( best_querybest[query], scorebest[score], candidatesall_scores, answeranswer, ) app.post(/api/optiquery/batch_ask) def batch_ask(questions: List[str]): results [] for idx, question in enumerate(questions): try: best, all_scores select_best_query(question) results.append({ index: idx, question: question, best_query: best[query], score: best[score], status: success, }) except Exception as exc: results.append({ index: idx, question: question, best_query: , score: 0.0, status: ffailed: {exc}, }) return {total: len(questions), results: results} if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8900)启动服务python api_service.py然后可以用 Python 脚本批量调用import requests url http://127.0.0.1:8900/api/optiquery/batch_ask questions [ 这个月的营收为什么下滑, 杭州区域订单超时原因, 客户投诉最多的三个问题是什么, ] resp requests.post(url, jsonquestions, timeout120) for item in resp.json()[results]: print(item[index], item[best_query], item[score], item[status])批量任务设计上有几个建议每条问题独立调用单条失败不影响整体。输出结果要带status字段方便定位失败原因。批量任务建议加日志记录原始问题、最优提问、分数、耗时。对耗时较长的任务用异步队列替代同步请求。7. 资源占用与性能观察“最优提问”是一个额外的处理层它一定会增加资源消耗。关键是要把增量成本控制在可接受范围内。主要资源消耗有三个点第一候选提问生成。调用一次 LLM。生成 N 个候选提问时token 消耗会明显上涨但通常只占最终回答成本的一小部分。可以控制候选数量在 3 到 5 个太少了没有对比太多了成本翻倍。第二目标函数评估。这里最容易失控。如果每个候选提问都调用“LLM 评分器”N 个候选就要 N 次额外调用。建议把 LLM 评分器只用于可执行性维度相关性和合规性用向量计算加规则判断能省掉大量调用。第三检索。如果每个候选提问都做一次向量检索也会增加延迟。正确做法是先用轻量方式给候选提问粗排序只对 top-2 或 top-3 做完整检索。观察资源占用时重点关注这几个指标从用户提问到获取上下文的总延迟目标函数打分阶段消耗的 token 数向量检索平均耗时如果本地跑 LLM用ollama ps或nvidia-smi观察显存占用批量任务模式下接口的 QPS 和失败率。如果发现延迟过高优先做两件事第一减少候选提问数量第二把 LLM 评分器替换成小型分类模型或规则引擎。8. 安全合规与使用边界“最优提问”从表面看只是查询改写但在企业落地时必须考虑安全合规边界。尤其当这个能力被用在“企业级 AI 智能体安全合规自动化检测系统”里提问本身就可能带来风险。一个直接的风险是候选提问生成过程会把用户原始问题和业务上下文发送给 LLM。如果 LLM 服务是外部 API就可能造成敏感数据外传。更稳妥的做法是敏感场景统一使用本地部署模型配置内网接口不经过外网。另一个风险是越权检索。候选提问可能被格式化成“包含客户 ID 的检索语句”如果权限系统不校验Agent 就能拿到本不该访问的数据。所以目标函数里的 Compliance 维度不能是摆设要对接真实的权限服务和敏感信息过滤规则。我给一个通用规则def compliance_gate(query: str, user_roles: List[str], allowed_kb: List[str]) - bool: # 解析 query 中涉及的实体和知识库范围 # 校验用户角色与权限 # 校验是否包含 PII 字段 return True # 实际项目按真实策略实现下面是几条必须遵守的边界涉及人脸、声音、客户隐私数据的场景必须确认数据来源合法并获得明确授权。检索企业内部文档时要按最小权限原则限制知识库范围。对外提供 API 服务时建议限制访问 IP 和调用频率避免被恶意刷量。批量任务要记录操作日志方便审计。不要把未脱敏的证件号、手机号、Token 拼进检索 query。9. 常见问题与排查方法问题现象可能原因排查方式解决方案候选提问生成结果雷同温度太低或提示词没有要求多样化查看生成的候选提问列表提高 temperature增加“拆分问题”“补充约束”等多样性要求检索召回结果为空query 与知识库语言不一致或切片粒度有问题打印候选提问和检索分数优化 query 改写规则调整知识库切片大小目标函数打分不稳定LLM 打分器输出波动固定评分标准多次调用看方差temperature 设为 0使用更详细的 rubric或多模型投票接口调用超时候选生成和 LLM 打分串行执行导致耗时过长查看日志中每阶段耗时加粗排序减少候选数量把 LLM 调用改为异步任务显存不足本地模型规模过大或并发任务过多使用nvidia-smi观察显存占用换量化模型降低并发分批处理批量任务中断没有断点续跑机制查看失败的任务索引任务列表加状态字段失败任务单独重试最优提问选出来后答案仍不对目标函数权重标定不合理对比不同权重下的 top-1 提问用历史数据集做权重回归测试合规过滤误杀正常问题敏感规则太过严格看合规拦截日志细化规则增加白名单10. 最佳实践与落地建议从“有一个目标函数”到“上线后真正提升 Agent 效果”中间还有几件值得注意的事。先跑通最小闭环。不要一上来就接入 LangGraph、分布式队列、复杂权限系统。先用一个本地模型、一个 Fake 知识库、一个 Python 脚本跑通“生成候选提问 - 目标函数打分 - 选择最优提问 - 生成答案”确认整体链路稳定再逐步加工程能力。保留一套可复现的评估集。准备 50 到 100 条历史问题每条标注“理想上下文”或“理想检索文档”。每次修改目标函数权重、提示词或模型版本后重新跑一遍评估集看命中率和答案满意度是否变化。这是目标函数调参最重要的依据。目标函数权重需要按业务标定。同样的公式客服场景和代码检索场景权重完全不同。客服场景更看重成本和用户追问体验代码检索场景更看重精确率和可执行性。不存在万能权重。配置、代码、数据分离。权重放配置文件候选提问模板放提示词管理模块用户问题和检索日志落库。这样后续做效果复盘时能还原每次提问的完整链路。给接口服务加监控。每次提问的记录都要保留原始问题、候选提问列表、每个候选的分数、最终选中的提问、检索上下文列表、最终答案、耗时。没有监控目标函数就是黑盒。关注“上下文利用率”。一个容易被忽略的指标是检索回来的上下文最终有多少被答案生成阶段实际使用。如果最优提问召回了 5 个文档但最终答案只引用了其中 1 个说明提问可能还是偏宽泛或者答案生成阶段的提示词没有强制要求使用检索结果。这套方法论最值得先验证的地方是在你现有 RAG 系统里把“用户原始问题”替换成“目标函数评估后的最优提问”对比两次检索结果的 hit rate 和最终答案质量。如果替换后有明显提升再继续投入成本做完整的目标函数服务如果提升不明显优先检查知识库切片质量和检索底座的召回能力而不是继续调权重。AI 智能体的上下文获取能力决定了它的上限。把“最优提问”做成一个可计算、可回归、可调参的工程模块比换一个更大的模型往往更能解决问题。下一步建议先跑通文中的最小 Demo再用你业务里的真实问题做一次对比测试这个动作能直接告诉你方向对不对。