1. 从一次 Agent 跑不通说起四大技术到底卡在哪AI Agent 开发入门最让人抓狂的地方不是概念听不懂而是每个概念都懂一点拼起来就跑不通。Prompt 写得挺像回事RAG 也把向量库建起来了Function Calling 的 JSON Schema 照着文档抄了MCP 的 Server 也启动了结果一联调模型不调用工具、检索回来的片段答非所问、MCP 客户端连不上 Server、报错信息还全是英文堆栈。这篇就聚焦一件事把 Prompt、RAG、Function Calling、MCP 这四块在工程里的接入位置和配置骨架讲清楚让你能复制粘贴出一套最小可跑的 Agent 基础链路。适合刚接触 Agent 开发、手里有一个模型 API Key、想快速跑通第一个能调用工具的 Agent 的开发者。全文以配置文件为主线settings.json、config.toml、.env 三种形态都会给到每一步都配验证动作跑不通就对照第 5 节的排查表。先说清楚四者在链路里的分工后面配置才不会乱技术在 Agent 里的角色典型配置文件验证方式Prompt大脑的指令层prompts/system.md单轮对话看输出格式RAG外挂知识库config.toml检索片段命中率Function Calling手脚调外部工具tools.json模型返回 tool_callsMCP工具接入的标准化协议mcp.jsonServer 握手成功2. 前置准备TaoToken 接入与项目骨架2.1 为什么用统一网关而不是直连各家Agent 开发阶段最烦的是模型换来换去今天用这个测 Prompt明天换那个测 Function Calling每换一家就要改 base_url、改鉴权头、改返回解析。用 TaoToken 这类统一网关的好处是OpenAI 兼容协议一套走到底切换模型只改一个 model 字段。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置里填这个就行。2.2 拿 Key 与目录结构登录后进控制台创建 API Key入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 的管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。Key 只显示一次复制到本地 .env别提交到 Git。项目骨架建议这样分四块技术各占一个目录互不干扰agent-demo/ ├── .env # 密钥不进版本库 ├── settings.json # 模型与运行参数 ├── config.toml # RAG 检索参数 ├── tools.json # Function Calling 声明 ├── mcp.json # MCP Server 注册 ├── prompts/ │ └── system.md # 系统提示词 └── src/ ├── llm.py # 统一模型调用 ├── rag.py # 检索增强 ├── tools.py # 函数执行 └── agent.py # 主循环2.3 .env 与 settings.json 骨架.env 只放密钥和网关地址TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/apisettings.json 放模型与运行参数这是整个 Agent 的入口配置{ model: gpt-4o-mini, base_url: https://taotoken.net/api, temperature: 0.2, max_tokens: 2048, timeout: 60, max_tool_rounds: 5, system_prompt_file: prompts/system.md }temperature 设 0.2 是因为 Agent 场景要的是稳定复现不是创意写作max_tool_rounds 限制工具调用轮数防止模型陷入无限调用。这两个参数后面排查死循环时会反复用到。3. Prompt 与 RAG 的配置骨架3.1 系统提示词写成文件而不是硬编码Prompt 是 Agent 的指令层最容易犯的错是把它塞在代码字符串里改一次要动代码。写成 prompts/system.md结构按「角色 任务 约束 输出格式」四段来# 角色 你是一个数据查询助手负责根据用户问题调用工具或检索知识库。 # 任务 1. 判断问题是否需要外部数据需要则调用对应工具 2. 不需要工具时基于检索片段回答 3. 无法确定时明确说信息不足不要编造 # 约束 - 只使用工具返回的真实数据禁止臆测 - 引用知识库内容时标注来源片段编号 # 输出格式 - 工具调用直接返回 function call不要额外解释 - 文本回答先给结论再给依据约束段里「禁止臆测」和「标注来源」这两条是压幻觉最有效的两句话比在代码里做后处理省事得多。3.2 RAG 的 config.tomlRAG 的接入位置在「模型调用之前」用户问题先过检索器命中的片段拼进 Prompt 再送给模型。config.toml 把检索参数集中管理[embedding] model text-embedding-3-small base_url https://taotoken.net/api batch_size 32 [retrieval] top_k 4 score_threshold 0.35 chunk_size 500 chunk_overlap 80 [store] type local path ./data/indextop_k 给 4 是经验值太少召回不全太多挤占上下文还引入噪声。score_threshold 是过滤低质量片段的闸门低于 0.35 的直接丢宁可少给也别给错。chunk_overlap 设 80 是为了缓解切片切断语义的问题让相邻块有重叠。3.3 检索与拼装的代码骨架import tomllib from openai import OpenAI with open(config.toml, rb) as f: cfg tomllib.load(f) client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) def retrieve(query: str, top_k: int 4): vec embed(query) hits index.search(vec, top_k) return [h for h in hits if h.score cfg[retrieval][score_threshold]] def build_prompt(query: str, hits: list) - str: context \n.join(f[{i}] {h.text} for i, h in enumerate(hits)) return f参考资料\n{context}\n\n用户问题{query}注意 build_prompt 里给每个片段编了号配合系统提示词里的「标注来源片段编号」模型回答时就能带上 [0][2] 这类引用方便你核对它到底有没有瞎编。4. Function Calling 与 MCP 的配置骨架4.1 tools.json 声明工具Function Calling 的接入位置在「模型返回之后」模型决定调哪个函数、传什么参数你的代码负责真正执行。tools.json 用标准 JSON Schema 声明{ tools: [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气, parameters: { type: object, properties: { city: { type: string, description: 城市名如 杭州 }, unit: { type: string, enum: [celsius, fahrenheit] } }, required: [city] } } } ] }description 写得越具体模型选错函数的概率越低。enum 限定取值范围能挡掉一批参数格式错误。4.2 工具执行与主循环import json with open(tools.json) as f: TOOLS json.load(f)[tools] def run_agent(messages: list): for _ in range(settings[max_tool_rounds]): resp client.chat.completions.create( modelsettings[model], messagesmessages, toolsTOOLS, temperaturesettings[temperature], ) msg resp.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for call in msg.tool_calls: args json.loads(call.function.arguments) result dispatch(call.function.name, args) messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse), }) return 达到最大工具调用轮数已中止这个循环就是 ReAct 范式的工程实现模型思考、发起调用、拿到观察结果、再思考。max_tool_rounds 是安全阀防止模型反复调同一个工具停不下来。4.3 mcp.json 注册 MCP ServerMCP 解决的是工具接入的标准化问题不用每接一个工具就手写一份 JSON SchemaServer 自己声明能力客户端自动发现。mcp.json 注册 Server{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, ./data], env: {} } } }MCP 的接入位置在「工具层之下」它把 Function Calling 的声明和执行都标准化了你的 Agent 主循环可以不变工具来源从手写 tools.json 换成 MCP Server 动态拉取。想深入看协议细节和更多 Server 示例可以翻接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。5. 验证请求与常见报错排查5.1 三步验证法第一步验证模型连通。用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 直接发一句「返回 JSON{ok:true}」确认 Key 和 base_url 没问题。第二步验证 Function Calling。发「杭州天气怎么样」看返回里有没有 tool_calls 字段resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 杭州天气怎么样}], toolsTOOLS, ) print(resp.choices[0].message.tool_calls)第三步验证 RAG。问一个只有你知识库里有答案的问题看回答里有没有片段编号引用。5.2 常见报错对照表报错现象大概率原因处理动作401 UnauthorizedKey 没读到或带空格检查 .env 是否被加载模型不返回 tool_callstools 没传或 description 太模糊打印请求体确认 tools 字段参数解析失败arguments 不是合法 JSON加 try 兜底并回灌错误信息RAG 答非所问top_k 太大或阈值太低调小 top_k调高 thresholdMCP 连接超时command 路径不对手动执行 command 验证循环停不下来工具返回空导致模型重试设 max_tool_rounds 上限其中「模型不返回 tool_calls」最常见八成是 tools 参数没传进去或者 description 写成了「查询天气」这种没有信息量的描述。改成「查询指定城市的实时天气返回温度和天气状况」立刻就好。5.3 长期编码场景的配置建议如果你是要把 Agent 接到日常编码或长任务里单次调用模式不够用需要能持续跑、能记住上下文的方案。这类场景可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 配置思路和上面一致只是把 max_tool_rounds 放宽、加上会话持久化。Claude Code 这类工具的接入方式在 https://taotoken.net/claudecode?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 有说明本质还是同一套 base_url Key 的配置。6. 把四块拼成一条能跑的链路回到开头那个跑不通的问题四块技术各自的配置骨架其实都不复杂难的是接入位置要对。Prompt 在最上层定规则RAG 在模型调用前做检索拼装Function Calling 在模型返回后做执行回灌MCP 在工具层做标准化。顺序错了就会出现「检索结果没进 Prompt」「工具返回没回灌给模型」这类看起来像模型笨、实际是链路断的问题。建议的推进顺序是先用 settings.json 跑通单轮对话再加 tools.json 验证 Function Calling然后接 config.toml 上 RAG最后用 mcp.json 把工具来源标准化。每加一块就跑一次第 5 节的验证动作别四块一起上再联调那样报错根本定位不到是哪一层。这套骨架跑通之后换模型、加工具、扩知识库都只是改配置的事主循环不用动。