多工具协同Agent实战:用LangChain构建越南市场情报系统

【导航台账】老蒋的技术博客全系列文章汇总(持续更新)

从单工具到多工具协同,一个20年IT老兵的Agent进阶之路

📌 本文是《从RAG到Agent:20年IT老兵的AI智能体实战笔记》专栏第3篇


一、引言:从“单工具”到“多工具协同”的能力跃迁

        在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 关键参数说明

参数作用
temperature0.1降低模型输出的随机性,提高工具调用决策的稳定性
max_iterations15防止Agent陷入无限循环,15次迭代足以完成4步工具调用
return_intermediate_stepsTrue返回中间步骤,提高解析器对模型输出的容错能力

五、运行测试: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的实践,我们完成了从“单工具调用”到“多工具协同”的能力跃迁。核心收获如下:

  1. 多工具协同的本质是“任务拆解+状态传递”:Agent需要自主决定工具调用顺序,并将中间结果传递给下游工具

  2. 工具描述比工具实现更重要:工具函数的名称、描述、参数说明,直接影响模型调用的准确性

  3. 参数调优是工程化的关键temperature=0.1 提升稳定性,return_intermediate_steps=True 增强解析容错

  4. 工程化规范是长期输出的保障:统一的文件头、异常处理、脱敏规范,让代码真正可复用、可传播

下篇预告

《Agent记忆系统:让AI从“金鱼脑”到“大象记忆”》

  • 短时记忆:ConversationBufferMemory的原理与实现

  • 长时记忆:用向量数据库构建Agent的“长期记忆”

  • 记忆管理策略:总结、压缩、遗忘的工程实践

  • 在C2的越南市场场景中,如何让Agent记住“上周已经生成过报告”?

敬请期待!


📌 本文完整代码:所有代码已在本地环境验证通过,可直接运行,关注&收藏,评论区【用LangChain构建越南市场情报系统】免费分享
📌 专栏主页从RAG到Agent:20年IT老兵的AI智能体实战笔记
📌 技术交流:欢迎在评论区留言,我会持续关注并回复。

作者:制造数据与AI践行者老蒋(javy21)
博客制造数据与AI践行者老蒋-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值