Agent 要落地,得接外部世界。纯聊天解决不了"查一下数据库里昨天的销量"。这一节讲 Agent 怎么通过函数安全地接数据库、调 API、消费 Web 服务——核心仍是第三章的函数调用,只是函数体换成真实 IO。

把数据库查询封装成函数,注册给 Agent。模型生成调用时只传参数,不碰 SQL 拼接细节(降低注入风险)。
import os def query_sales(yesterday: str) -> str: """查询指定日期的销售总额。""" # 真实场景用参数化查询,绝不字符串拼接 sql = "SELECT SUM(amount) FROM orders WHERE day = %s" # cursor.execute(sql, (yesterday,)) -> 返回结果 return "昨日销售额 12800 元" # 示例返回 # 注册后,Agent 可用自然语言触发:查一下昨天的销售额
多个函数都调内部服务时,抽一个客户端对象,函数只调它的方法。这样鉴权、重试、超时集中管理,Agent 侧保持干净。
class InternalClient: def __init__(self, token): self.token = token def get_user(self, uid: str): # 带鉴权请求内部用户服务 return {"uid": uid, "name": "示例"} client = InternalClient(os.environ.get("SVC_TOKEN")) def fetch_user(uid: str) -> str: """获取用户信息。""" return str(client.get_user(uid)) # Agent 注册 fetch_user,自然能回答"用户123是谁"
外部 Web 服务会抖。函数里必须设超时和有限重试,否则一次慢响应卡住整个对话。我们建议重试不超过 3 次、超时 10 秒。
import requests def call_web_api(endpoint: str) -> str: """调用外部 Web 接口,带超时与重试。""" for i in range(3): try: r = requests.get(endpoint, timeout=10) return r.text[:200] except requests.Timeout: if i == 2: return "调用超时,请稍后" return "调用失败"
能接不等于该接。我们给一条边界原则:Agent 该接的是"它做不了、外部系统能做"的事——查实时数据、执行写操作、调专有服务;不该接的是"它本就能在对话里解决"的事,比如纯文本润色硬要套个 API。每多一个外部依赖,就多一份版本耦合、超时风险和故障面,所以接之前先问"这部能力离得开框架吗、稳定吗、出错了能隔离吗"。
用生物来类比:共生关系要互利且稳定才该接纳,一个动不动罢工的共生体只会拖垮宿主。判断一个集成值不值得引入,看三点——它解决的是不是编排层解决不了的、它的维护节奏跟不跟得上框架、它的错误能不能被你的兜底捕获而不蔓延。三点有一个不满足,就先别接,用更轻的方式(比如先把结果缓存进来)过渡。
还有个生产必做的设计:失败要"就地转消息"。外部调用超时、返回异常、鉴权失效,都应当在函数内转成一句可读的错误字符串回给对话(前面 call_web_api 的"调用超时,请稍后"就是这个思路),而不是让异常冒泡中断整段对话。这样 Agent 能在下一轮自己决定"重试还是换个说法问用户",系统韧性来自这里。
| 该接 | 不该接 |
|---|---|
| 查实时数据、执行写操作、调专有服务 | 对话内就能做的文本处理 |
| 稳定、错误可隔离的依赖 | 动不动罢工、错误会蔓延的依赖 |
| 编排层做不了的能力 | 仅为"显得高级"而加的调用 |
外部集成一多,凭据和版本就成了隐患。凭据上,所有 key、token 必须走环境变量或专用密钥管理,绝不进函数体、不进仓库——前面示例里 client = InternalClient(os.environ.get("SVC_TOKEN")) 就是这个原则。更稳妥的是按服务分桶管理,哪个服务泄露就只换那一个,不牵连全局。多框架(7.2)各自都可能要凭据,集中在一个配置层统一注入,别让各框架散落各处各管各的,否则漏配、错配都难查。
依赖上,AutoGen、LangChain、LlamaIndex 以及你的数据库驱动,都在各自迭代,某个接口三个月一变。集成代码必须锁版本、写清适配区间,升级时单独跑集成回归测试,而不是随手装最新。我们见过团队"pip install --upgrade"后整个检索链路失效,排查半天才发现是某框架改了返回结构。治理的底线:集成相关的依赖,视同生产依赖严管,不随意浮动。
| 治理项 | 做法 | 不做 |
|---|---|---|
| 凭据 | 环境变量/密钥管理、按服务分桶 | 硬编码进函数或仓库 |
| 版本 | 锁版本、写适配区间、单独回归 | 随手 upgrade 全量 |
| 配置 | 统一配置层注入 | 各框架散落各配 |
接入外部系统后,别只靠手跑验证。给每个外部函数写测试:用mock替代真实数据库/API,验证"参数正确时返回预期、参数异常时转成错误字符串、超时有兜底"。这样框架或依赖升级时,跑一遍集成测试就能知道哪个外部调用被打破。集成测试是外部依赖频繁变动时的安全网,省去每次升级后的手工惊吓。
⚠️ 函数里用字符串拼接 SQL 且参数来自模型,等于敞开 SQL 注入,务必参数化。
💡 外部集成让"对话即编排"从聊天室走进业务系统——手伸出去,才真正干活。