1.背景
要学习AI agent,只会用agent 框架,还不够,一旦框架出现问题,没法快速的排查出问题。 学习就应该“知其然,更应该知其所以然” ,今天我们就用编码的方式实现一个简单的agent 。我们模拟一套AI学生评价系统,通过自然语言查询数据、分析数据。
2.环境
python 版本:3.11
LLM: deepseek-chat(有 api key)
SDK:openai 1.63.2
若不知道,怎么获取“deepseek api key”,辛苦爬楼看一下,我前面的文章
3.步骤
1)定义prompt
REACT_PROMPT = """
--你在思考、行动、行动输入、暂停、观察的循环中运行。
--在循环结束时,输出一个答案
--用Thought来描述你对被问到的问题的想法。
--使用Action运行一个可用的操作
--使用动作输入来指示动作的输入-然后返回"等待,处理中"。
--观察将是运行这些操作的结果。
你可用的工具有:
{tools}
Rules:
1-如果输入是问候或再见,直接以友好的方式回复,不要使用思考-行动循环。
2-否则,按照思考-行动-输入循环去寻找最佳答案。
3-如果你已经有了问题的一部分或整个的答案,不要依赖于外部行为,使用你的知识。
4-如果你需要执行多个Action,在单独的调用中执行。
5-最后,给出一个最终的答案。
Some examples:
###
Question: 今天北京天气怎么样?
Thought: 我需要调用 get_weather 工具获取天气
Action: get_weather
Action Input: {"city": "BeiJing"}
等待,处理中
You will be called again with this:
Observation: 北京的气温是0度.
You then output:
Final Answer: 北京的气温是0度.
Begin!
New input: {input}"""
注意prompt 规则说清楚,让模型逐步处理, 可以减少幻觉
2)定义工具
tools = [
{
"name": "get_score_by_name",
"description": "使用该工具获取指定员工的绩效评分",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "员工名字",
}
},
"required": ["name"]
},
}
]
def get_score_by_name(name):
if name == "张三":
return "name: 张三 绩效评分: 85.9"
elif name == "李四":
return "name: 李四 绩效评分: 92.7"
else:
return "未搜到该员工的绩效"
4)写LLM 对接client
client = OpenAI(
api_key="sk-xx自己申请",
base_url="https://api.deepseek.com/v1"
)
def send_messages(messages):
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages
)
return response
5)写agent 逻辑
if __name__ == "__main__":
query = "张三和李四的绩效谁好?"
prompt = REACT_PROMPT.replace("{tools}", json.dumps(tools)).replace("{input}", query)
messages = [{"role": "user", "content": prompt}]
#进入无限循环,直到满足某个结束条件
while True:
response = send_messages(messages)
response_text = response.choices[0].message.content
print("大模型的回复:")
print(response_text)
final_answer_match = re.search(r'Final Answer:\s*(.*)', response_text)
# 有“Final Answer:” 退出循环
if final_answer_match:
final_answer = final_answer_match.group(1)
print("最终答案:", final_answer)
break
messages.append(response.choices[0].message)
# 使用正则表达式从response_text中提取Action字段的值
# 该值用于标识所需的操作
action_match = re.search(r'Action:\s*(\w+)', response_text)
# 使用正则表达式从response_text中提取Action Input字段的值
# 该值用于提供操作所需的输入参数,可以是JSON对象或字符串
action_input_match = re.search(r'Action Input:\s*({.*?}|".*?")', response_text, re.DOTALL)
if action_match and action_input_match:
tool_name = action_match.group(1)
params = json.loads(action_input_match.group(1))
observation = ""
if tool_name == "get_score_by_name":
observation = get_score_by_name(params['name'])
print("接口查询结果:Observation:", observation)
elif tool_name =="无法识别":
print("接口查询结果:Observation:", "无法识别")
# 将观察结果作为用户角色的内容添加到消息中
messages.append({"role": "user", "content": f"Observation: {observation}"})
4.成果
5.总结
1)注意prompt 逻辑、demo要写清楚,同时让模型按规则逐步处理, 可以减少幻觉
2)核心逻辑是用代码,在合适的时间调度大模型、工具,然后组装获取到最终结果
3)本case就涉及多次调用大模型,而且追加了上下文(message),让大模型不断地通过追加内容,生成最终结果,在程序中要判断,已经生产最终成果,然后输出结果,退出循环。