TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

不用框架手写一个智能体

不用框架手写智能体:system 提示词塞工具描述和循环规则,用 stop 参数掐住幻觉 Observation,手动执行函数后拼接续写。

智能体框架把循环、解析、重试都包好了,但也把机制藏了起来。想看清智能体的本质,最好的办法是脱掉框架、用最原始的方式手写一个:一个 Python 函数当工具,一个无服务器 API 当大脑,剩下的循环逻辑全部自己拼 [1]。这套写法不适合上生产,但它是理解智能体工作原理的最佳起点 [1]。

大脑:无服务器推理接口

Hugging Face 生态的 Serverless API 免安装免部署,一个客户端直接对大量模型做推理。取一个 read 权限的 token,建客户端时指定模型 [1]:

import os
from huggingface_hub import InferenceClient

## You need a token from https://hf.co/settings/tokens, ensure that you select 'read' as the token type.
## If you run this on Google Colab, add it in the "Secrets" tab (key icon on the left sidebar) and call it "HF_TOKEN".
try:
    from google.colab import userdata
    HF_TOKEN = userdata.get("HF_TOKEN")
except ImportError:
    HF_TOKEN = os.environ.get("HF_TOKEN")

client = InferenceClient(model="moonshotai/Kimi-K2.5", token=HF_TOKEN)

调用一律走 chat 方法:它替你套聊天模板,也是跨模型迁移时最省心的方式 [1]:

output = client.chat.completions.create(
    messages=[
        {"role": "user", "content": "The capital of France is"},
    ],
    stream=False,
    max_tokens=1024,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)

智能体的心脏:一条 system 提示词

智能体库的核心就是把信息追加进 system 提示词。智能体版 system 提示词比普通聊天多两样东西:工具描述和循环规则——Thought → Action → Observation 的固定节奏 [1]:

SYSTEM_PROMPT = """Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have an `action` key (with the name of the tool to use) and an `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use :

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}


ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. """

细读这段提示词,每句话都在堵一个漏洞:工具用 JSON blob 调用,action 字段只允许列出的工具名;Action 一次只能一个;Thought/Action/Observation 可以循环 N 次;结尾必须是固定的 Final Answer 格式——连字面字符都强调要精确匹配,因为客户端要靠这个字符串判断循环结束 [1]。

用户问题追加在 system 之后,这一步由 chat 方法完成 [1]:

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "What's the weather in London?"},
]

print(messages)

第一次调用:撞上幻觉

直接发起调用,模型给出了教科书式的回答 [1]:

output = client.chat.completions.create(
    messages=messages,
    stream=False,
    max_tokens=200,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)

输出里有 Thought、Action 的 JSON blob,紧接着一条 Observation:伦敦多云、12°C——问题是 get_weather 这个函数根本没被执行。模型自导自演了整场戏:自己编了工具调用,又自己编了工具结果。这就是智能体开发里最经典的幻觉场景:Observation 必须来自真实世界,而模型会顺手伪造它 [1]。

解法是在 Observation 之前掐断生成:stop 参数设为 Observation:,让模型只产出 Thought 和 Action,函数由客户端真实执行,输出再以 Observation 的身份回填 [1]:

# The answer was hallucinated by the model. We need to stop to actually execute the function!
output = client.chat.completions.create(
    messages=messages,
    max_tokens=150,
    stop=["Observation:"], # Let's stop before any actual function is called
    extra_body={'thinking': {'type': 'disabled'}},
)

print(output.choices[0].message.content)

这次输出干净地停在 Action 的 JSON blob 上,幻觉没有机会出现 [1]。

第二轮:真实结果回填

写一个占位的天气函数——真实场景里这里换成真正的 API 调用 [1]:

# Dummy function
def get_weather(location):
    return f"the weather in {location} is sunny with low temperatures. \n"

get_weather('London')

第二轮调用把四样东西拼进消息列表:system 提示词、用户问题、上一轮被截断的模型输出(补上 Observation: 前缀)、以及函数真实返回值作为 Observation [1]:

messages=[
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "What's the weather in London ?"},
    {"role": "assistant", "content": output.choices[0].message.content + "Observation:\n" + get_weather('London')},
]

output = client.chat.completions.create(
    messages=messages,
    stream=False,
    max_tokens=200,
    extra_body={'thinking': {'type': 'disabled'}},
)

print(output.choices[0].message.content)

模型读到真实的 Observation 后,按 system 提示词规定的格式收尾:Final Answer: The weather in London is sunny with low temperatures. [1]

循环的本质

把这两轮展开看,智能体的全部机制就是三步循环:模型产出 Thought 和 Action,客户端在 Observation: 处截断并执行真实函数,把结果拼回消息列表再调用一次——如此往复直到模型输出 Final Answer [1]。这个最小实现暴露了所有框架在替你做的事:解析 JSON blob、分发函数调用、维护消息列表、判断循环终止、处理格式偏差。手写一遍的价值也在这里——之后再读 smolagents、LangGraph 这类框架的源码,每一层抽象都能对上刚才亲手拼过的那块积木 [1]。

框架不是魔法,只是把这个循环写得更健壮:错误解析时重试、支持并行工具调用、提供结构化的工具注册机制。但循环的骨架——截断、执行、回填、续写——和这篇手写版一模一样 [1]。

本文改编自 Hugging Face Agents Course 的 Dummy Agent Library 一节[1],原文以 Apache-2.0 许可证发布。

来源

  1. 1
    Dummy Agent Library | Hugging Face Agents CourseHugging Face agents-course (Apache-2.0)

引用本页: 不用框架手写一个智能体. TellWorld, https://www.tellworld.net/zh/practice/practice-agent-dummy-library. 更新于 2026-11-14

相关动态