1. 项目概述:构建不依赖Function Call的AI Agent工作流
在当前的AI应用开发中,大模型的功能调用(function call/tool call)能力已经成为构建复杂Agent系统的标配。但现实情况是,许多开源模型(如本地部署的Ollama模型)或特定场景下的模型并不支持这一功能。这就引出了一个关键问题:如何在模型原生不支持工具调用的前提下,依然实现完整的Agent工作流?
本文介绍的解决方案通过精心设计的提示工程和输出解析机制,在不依赖模型原生function call能力的情况下,实现了完整的ReAct(Reasoning and Acting)工作流。这个方案具有以下核心价值:
- 模型兼容性:适配任何不支持function call的大语言模型,包括各类开源模型
- 完整工作流:保留完整的思考-行动-观察循环,不牺牲Agent的推理能力
- 灵活扩展:采用模块化设计,工具集可自由扩展,适用于不同业务场景
- 本地部署友好:特别适合需要私有化部署的场景,不受云端API限制
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心设计思路解析
2.1 状态机模型设计
整个系统基于状态机模型构建,核心状态流转如下:
python复制@dataclass
class InputState:
"""输入状态类"""
messages: Annotated[Sequence[AnyMessage], add_messages] = field(
default_factory=list
)
@dataclass
class State(InputState):
"""扩展的状态类"""
is_last_step: IsLastStep = field(default=False)
状态设计的关键点:
- 消息序列:保存完整的对话历史,包括用户输入、AI响应和工具返回结果
- 步骤控制:通过
is_last_step标记终止条件,防止无限循环 - 不可变设计:使用dataclass确保状态变更可追踪,便于调试
2.2 ReAct提示工程
系统通过精心设计的提示模板引导模型输出结构化响应:
python复制REACT_PROMPT = '''
你被设计用于帮助完成各种输入任务...
## 输出格式
如果本次需要使用工具完成某个子任务,请按照以下格式输出:
Thought: 我需要使用一个工具来帮助回答这个问题。
Action: 工具名称 (从 {tool_names} 中选择一个工具)
Action Input: 传递给工具的输入,使用 JSON 格式表示参数...
注意:
* 始终以"Thought"开头
* 使用有效的JSON格式作为Action Input
* 一次响应最多只能使用一个工具
'''
提示设计的关键技巧:
- 严格格式控制:要求模型必须按照指定格式响应
- 错误预防:明确提示JSON格式要求,避免常见解析错误
- 单步限制:每次只处理一个工具调用,降低复杂度
3. 关键实现细节
3.1 模型调用与输出解析
核心的模型调用方法通过以下步骤实现结构化解析:
python复制async def call_model(self, state: State, config: RunnableConfig) -> Dict[str, List[AIMessage]]:
# 生成工具描述和提示
tools_desc = "\n".join([f"- {tool.name}: {tool.description}" for tool in self.tools])
system_prompt = self.REACT_PROMPT.format(tools_desc=tools_desc, tool_names=[t.na
