从单工具到多工具协同,一个20年IT老兵的Agent进阶之路
📌 本文是《从RAG到Agent:20年IT老兵的AI智能体实战笔记》专栏第3篇
-
专栏简介:20+年IT老兵,从RAG到Agent系统进阶。聚焦制造业智能问答客服、数据质量巡检、数仓分层设计、知识库检索增强、设备智能化等场景,分享可复用的工程化代码与实践心得。
一、引言:从“单工具”到“多工具协同”的能力跃迁
在C1《我的第一个工具调用Agent》中,我们实现了一个能调用计算器、时间查询、文件读写、知识库检索四项能力的Agent,让AI第一次“动了起来”。
但现实世界的业务问题,远比“现在几点了?”复杂得多。
以制造业数据管理场景为例:
-
“帮我查一下胡志明市所有设备的在线率,结合近期汇率波动,生成一份市场运营简报”——这涉及设备数据查询、汇率分析、市场情报检索、报告生成四个步骤
-
如果Agent只能调用单个工具,这个问题就无法解决
-
我们需要的是:Agent能自主拆解复杂任务,按正确顺序调用多个工具,并将中间结果传递下去
C2的核心目标,就是构建一个具备多工具协同能力的Agent,让它能像人类一样完成“感知→分析→报告”的完整工作流。

本文基于“智联工坊”虚拟智能工厂的越南市场场景,完整记录了一个多工具协同Agent从设计到实现的全过程。
二、场景还原:智联工坊的“越南之困”
2.1 智联工坊是谁?
智联工坊是虚拟的智能工厂,是一家50人规模的物联网科技公司,产品涵盖商用智能交互屏、智能环境监测终端、资产追踪定位器等。其商用智能交互屏(纸巾机)曾在国内铺货5万+台,并成功出海越南市场。
2.2 越南市场的真实痛点
智联工坊的商用智能交互屏曾经进入越南市场,在胡志明市、河内、岘港三城部署了近1500台设备。团队很快遇到了几个棘手问题:
| 痛点 | 具体表现 |
|---|---|
| 设备运营盲区 | 设备在线率、扫码成功率无法实时感知,故障全靠客户电话投诉 |
| 汇率波动影响 | 越南盾兑人民币汇率波动剧烈,直接影响设备成本和利润,但缺乏自动监控 |
| 竞品信息滞后 | 越南本地竞品动态、支付政策变化无法及时获取,决策全靠人肉搜索 |
| 报告生成低效 | 每周需要人工汇总数据,撰写市场简报,耗时2-3小时 |
核心问题:如果有一个AI Agent能自动完成“设备监控→汇率分析→情报检索→报告生成”的闭环,团队就能从繁琐的手工工作中解放出来。
这正是C2要解决的问题。
三、技术方案:四大工具链的设计与实现
C2的Agent架构遵循“感知→分析→报告”三层逻辑,由四个工具协同完成:
用户提问
↓
【感知层】MQTT设备状态模拟器 → 获取1500台设备运营数据
↓
【分析层】汇率查询器 + 市场情报检索器 → 获取外部环境数据
↓
【报告层】多语言报告生成器 → 生成结构化情报报告
↓
返回结果
工具1:MQTT设备状态模拟器(tools/mqtt_simulator.py)
@tool
def fetch_vietnam_device_status(city: str = None) -> str:
"""
功能描述:获取越南地区设备的运营状态数据。
设计思路:
1. 模拟越南三城(胡志明市、河内、岘港)共1500台设备的数据上报
2. 每城500台,在线率88%,贴近真实IoT场景
3. 返回设备总数、在线率、平均扫码成功率、交易量等运营指标
Args:
city (str, optional): 城市名,如 'Ho Chi Minh City'
Returns:
str: JSON格式的设备汇总报告
"""
try:
if city and city not in CITIES:
return json.dumps({
"error": f"未知城市 '{city}'",
"suggestion": f"请使用以下城市名之一: {', '.join(CITIES)}"
}, ensure_ascii=False)
target_cities = [city] if city else CITIES
all_devices = []
for c in target_cities:
for i in range(DEVICE_COUNT_PER_CITY):
all_devices.append(_generate_device_data(c, i))
total = len(all_devices)
online = sum(1 for d in all_devices if d["status"] == "online")
avg_success = sum(d["scan_success_rate"] for d in all_devices) / total
report = {
"region": city or "Vietnam (All Cities)",
"total_devices": total,
"online_rate": f"{online/total*100:.1f}%",
"avg_scan_success": f"{avg_success*100:.1f}%",
"total_transactions_today": sum(d["total_transactions"] for d in all_devices),
"sample_devices": all_devices[:3]
}
return json.dumps(report, indent=2, ensure_ascii=False)
except Exception as e:
return json.dumps({
"error": f"设备状态获取失败: {str(e)}",
"suggestion": "请稍后重试,或检查网络连接"
}, ensure_ascii=False)
设计要点:
-
每城500台设备(共1500台)的规模,源于智联工坊越南市场的真实铺货量
-
在线率88%的设定,模拟了真实IoT场景中设备因网络、电力等因素导致的离线率
-
结构化错误返回(
error+suggestion),让Agent能理解并处理异常
工具2:越南盾汇率查询器(tools/exchange_rate.py)
@tool
def get_vietnam_exchange_rate(base_currency: str = "CNY") -> str:
"""
功能描述:获取越南盾(VND)兑指定货币的汇率及近期走势。
设计思路:
1. 模拟汇率数据(1 CNY ≈ 3400-3600 VND),在合理区间内随机浮动
2. 生成过去7天的模拟历史数据,展示"上涨"或"下跌"趋势
3. 支持 CNY 和 USD 两种基础货币
Args:
base_currency (str): 基础货币,'CNY' 或 'USD',默认 'CNY'
Returns:
str: JSON格式的汇率数据及历史趋势
"""
try:
if base_currency not in ["CNY", "USD"]:
return json.dumps({
"error": f"不支持的基础货币 '{base_currency}'",
"suggestion": "请使用 'CNY' 或 'USD'"
}, ensure_ascii=False)
base_rate = 3500.0
current_rate = round(random.uniform(base_rate - 100, base_rate + 100), 2)
# 生成7天历史数据
history = []
for i in range(6, -1, -1):
day_rate = current_rate * (1 + random.uniform(-0.03, 0.03))
history.append({
"date": (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d"),
"rate": round(day_rate, 2)
})
result = {
"base_currency": base_currency,
"target_currency": "VND",
"current_rate": current_rate,
"trend": "上涨" if history[-1]["rate"] > history[0]["rate"] else "下跌",
"history_7d": history,
"updated_at": datetime.now().isoformat()
}
return json.dumps(result, indent=2, ensure_ascii=False)
except Exception as e:
return json.dumps({
"error": f"汇率获取失败: {str(e)}",
"suggestion": "请稍后重试"
}, ensure_ascii=False)
设计要点:
-
同时支持CNY和USD,适配不同角色的决策需求(财务看USD,业务看CNY)
-
7天历史趋势让Agent能判断“上涨”或“下跌”,而不仅仅是当前值
工具3:越南市场情报检索器(tools/vietnam_news.py)
# 模拟新闻数据库
MOCK_NEWS_DB = {
"payments": [{
"title": "越南电子支付市场2026年预计增长25%",
"source": "Vietnam Economic Times",
"date": "2026-07-10",
"summary": "越南央行数据显示,2026年上半年电子支付交易额同比增长25%。"
}],
"vending": [{
"title": "胡志明市便利店密度超越新加坡,新零售设备需求激增",
"source": "Saigon Business Review",
"date": "2026-07-09",
"summary": "胡志明市便利店数量已超过新加坡,自助终端设备需求旺盛。"
}],
"general": [{
"title": "越南2026年Q2 GDP增长6.2%,制造业表现强劲",
"source": "Vietnam Investment Review",
"date": "2026-07-07",
"summary": "越南经济持续复苏,制造业与服务业成为主要增长引擎。"
}]
}
@tool
def search_vietnam_market_intelligence(topic: str = "general") -> str:
"""检索越南市场新闻与商业情报。"""
key = TOPIC_MAP.get(topic, "general")
results = MOCK_NEWS_DB.get(key, MOCK_NEWS_DB["general"])
return json.dumps({
"topic": topic,
"total_results": len(results),
"news": results,
"retrieved_at": datetime.now().isoformat()
}, indent=2, ensure_ascii=False)
设计要点:
-
主题分类(支付、新零售、汇率、通用),模拟真实情报系统的分类逻辑
-
预留了未来对接真实新闻API的扩展接口
工具4:多语言报告生成器(tools/report_generator.py)
@tool
def generate_market_report(device_summary: str, exchange_rate_data: str, news_data: str) -> str:
"""
功能描述:生成越南市场情报报告(Markdown格式)。
设计思路:
1. 整合设备数据、汇率数据、新闻数据
2. 生成中文报告主体,并调用百度翻译API生成越南语摘要
3. 报告自动保存到 workspace/reports/ 目录
Returns:
str: 报告保存路径
"""
# 解析JSON,提取关键信息
# 生成中文报告主体
# 调用翻译生成越南语摘要
# 保存Markdown文件并返回路径
return f"报告已生成:{filepath}"
设计要点:
-
Markdown格式便于人工阅读,也便于后续转换为PDF或HTML
-
百度翻译API集成,体现“出海”场景的真实需求
-
报告保存到workspace/reports/目录,形成可追溯的历史文档
四、Agent构建:让AI学会“规划”而非“执行”
4.1 Agent类型选择
在C1中,我们使用了 CONVERSATIONAL_REACT_DESCRIPTION,它支持单参数工具(如 calculate("3+5"))。
但在C2中,部分工具需要多个参数。例如 generate_market_report 需要三个输入参数,因此升级为 STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,该类型支持多参数工具(Agent会要求模型输出JSON格式的参数)。
4.2 System Prompt设计
为了让Agent按照业务逻辑顺序执行,我们在 agent/prompts.py 中定义了专用System Prompt:
VIETNAM_MARKET_SYSTEM_PROMPT = """你是一位拥有20年经验的物联网与海外市场运营专家。
你正在为"智联工坊"公司分析越南市场业务。
你的工作流程(必须遵循):
1. 当用户询问越南市场状况时,首先调用 fetch_vietnam_device_status 获取设备运营数据
2. 根据设备数据中的异常,主动调用 get_vietnam_exchange_rate 和 search_vietnam_market_intelligence
3. 综合所有数据,调用 generate_market_report 生成结构化报告
重要原则:
- 数据驱动:先获取数据,再做分析
- 按序执行:必须按照 设备状态 → 汇率 → 新闻 → 报告 的顺序执行
"""
4.3 Agent构建代码
def create_agent(verbose: bool = False):
llm = Ollama(
model=MODEL_NAME,
base_url=OLLAMA_BASE_URL,
temperature=0.1, # 调低温度,提高决策稳定性
system=VIETNAM_MARKET_SYSTEM_PROMPT,
)
tools = [
fetch_vietnam_device_status, # 1. 先看设备
get_vietnam_exchange_rate, # 2. 再看汇率
search_vietnam_market_intelligence, # 3. 再看新闻
generate_market_report, # 4. 最后生成报告
]
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
)
agent = initialize_agent(
tools,
llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
memory=memory,
verbose=verbose,
handle_parsing_errors=True,
max_iterations=15,
return_intermediate_steps=True, # 关键参数:提高解析稳定性
)
return agent
4.4 关键参数说明
| 参数 | 值 | 作用 |
|---|---|---|
temperature | 0.1 | 降低模型输出的随机性,提高工具调用决策的稳定性 |
max_iterations | 15 | 防止Agent陷入无限循环,15次迭代足以完成4步工具调用 |
return_intermediate_steps | True | 返回中间步骤,提高解析器对模型输出的容错能力 |
五、运行测试:Agent的“思考链”全记录

启动Agent后,输入 查询越南盾汇率,观察完整的思考链:
你: 查询越南盾汇率
> Entering new AgentExecutor chain...
Action:
{
"action": "get_vietnam_exchange_rate",
"action_input": {
"base_currency": "CNY"
}
}
Observation: {
"base_currency": "CNY",
"target_currency": "VND",
"current_rate": 3506.57,
"trend": "上涨",
"history_7d": [
{"date": "2026-07-08", "rate": 3487.95},
{"date": "2026-07-09", "rate": 3530.38},
...
]
}
Thought: 我已经获取了汇率数据,用户需要的是查询结果,我可以直接返回。
Final Answer: 当前越南盾兑人民币汇率为 3506.57,趋势为上涨。
关键观察:
-
Agent首先思考需要什么工具 → 决定调用
get_vietnam_exchange_rate -
工具返回数据后,Agent评估是否满足需求 → 直接返回结果
-
整个过程符合ReAct模式:思考→行动→观察→回答
六、踩坑记录:三个关键问题的解决
坑1:run() not supported when there is not exactly one output key
现象:启用 return_intermediate_steps=True 后,agent.run(user_input) 报错。
❌ 错误: `run` not supported when there is not exactly one output key.
Got ['output', 'intermediate_steps'].
原因:run() 方法要求返回值只有一个 output 键,而启用 return_intermediate_steps 后,返回值包含 output 和 intermediate_steps 两个键。
解决:改用 agent.invoke({"input": user_input})["output"]。
# 修改前(报错)
response = agent.run(user_input)
# 修改后(正常)
result = agent.invoke({"input": user_input})
response = result["output"]
💡 经验:LangChain官方已推荐
invoke()替代run(),C2的实践也印证了这一点。
坑2:Could not parse LLM output
现象:Agent在调用工具时,偶尔输出格式不符合解析器的预期。
Could not parse LLM output: Action:
```json
{"action": "get_vietnam_exchange_rate", ...}
**原因**:Qwen2.5:7B作为开源模型,输出的JSON偶尔会包含多余的markdown标记或文本。
**解决**:
- 将 `temperature` 从0.3降至0.1,减少输出的随机性
- 启用 `return_intermediate_steps=True`,增强解析器的容错能力
### 坑3:跨文件系统路径映射问题
**现象**:在PyCharm中,代码报“未解析的引用”,但命令行运行正常。
**原因**:项目代码在Windows文件系统(`/mnt/d/...`),而Python解释器在WSL中,两者路径前缀不一致。
**解决**:在PyCharm中配置路径映射(Path Mappings),将Windows路径映射到WSL路径。或采用方案二:将项目代码迁移到WSL原生文件系统。
> 💡 **经验**:对于跨平台开发环境,路径映射是绕不开的必修课。
## 七、工程化实践:V1.2代码规范的落地
在C2的开发过程中,我同步升级了专栏的代码规范至 **V1.2版本**,核心变化包括:
### 7.1 文件头模板
每个Python文件开头包含核心问题、解决方案推演、业务场景溯源:
```python
"""
============================================================================
# 制造数据与AI践行者老蒋 | CSDN: https://blog.csdn.net/javy21
# 文件名: config.py
# 核心问题: 集中管理所有配置项
# 解决方案推演 (Why this way?):
# 1. 方案选择: 使用 python-dotenv 加载 .env 文件
# 2. 关键设计: 所有路径使用 pathlib.Path,确保跨平台兼容
# 3. 边界思考: 启动时校验关键配置,缺失时给出友好警告
# 案例溯源: "智联工坊"虚拟工厂 - 商用智能交互屏 项目
# 海外场景: 越南市场设备MQTT数据处理
============================================================================
"""
7.2 AI Agent异常处理规范
工具函数在异常时返回结构化错误,而非抛出堆栈:
return json.dumps({
"error": f"设备状态获取失败: {str(e)}",
"suggestion": "请稍后重试,或检查网络连接"
}, ensure_ascii=False)
7.3 业务场景脱敏规范
所有案例统一映射至 “智联工坊”虚拟工厂体系,杜绝真实企业信息泄露。
八、总结与下篇预告
总结
通过C2的实践,我们完成了从“单工具调用”到“多工具协同”的能力跃迁。核心收获如下:
-
多工具协同的本质是“任务拆解+状态传递”:Agent需要自主决定工具调用顺序,并将中间结果传递给下游工具
-
工具描述比工具实现更重要:工具函数的名称、描述、参数说明,直接影响模型调用的准确性
-
参数调优是工程化的关键:
temperature=0.1提升稳定性,return_intermediate_steps=True增强解析容错 -
工程化规范是长期输出的保障:统一的文件头、异常处理、脱敏规范,让代码真正可复用、可传播
下篇预告
《Agent记忆系统:让AI从“金鱼脑”到“大象记忆”》
-
短时记忆:ConversationBufferMemory的原理与实现
-
长时记忆:用向量数据库构建Agent的“长期记忆”
-
记忆管理策略:总结、压缩、遗忘的工程实践
-
在C2的越南市场场景中,如何让Agent记住“上周已经生成过报告”?
敬请期待!
📌 本文完整代码:所有代码已在本地环境验证通过,可直接运行,关注&收藏,评论区【用LangChain构建越南市场情报系统】免费分享。
📌 专栏主页:从RAG到Agent:20年IT老兵的AI智能体实战笔记
📌 技术交流:欢迎在评论区留言,我会持续关注并回复。
作者:制造数据与AI践行者老蒋(javy21)
博客:制造数据与AI践行者老蒋-CSDN博客

441

被折叠的 条评论
为什么被折叠?



