Un agente sobre tu base de datos
Traducción automática
Esta página se tradujo automáticamente del inglés y todavía no la revisó un hablante nativo; las correcciones son bienvenidas en GitHub. Si algo no coincide, vale el original en inglés.
Tarea: dejar que un asistente responda "¿qué productos se están agotando?" a partir de la base de datos de la tienda, sin dejar nunca que el modelo escriba SQL.
# Recipe: an agent that answers from your database through tools you write.
paths:
components: ./components
migrations: ./migrations
datasources:
db:
driver: sqlite
database: ./data/shop.db
llm:
model: phi3CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
stock INTEGER NOT NULL
);
INSERT INTO products (name, stock) VALUES
('Mug', 40), ('Monitor', 2), ('Cable', 3), ('Keyboard', 25);La herramienta es una función que tú escribes, con una consulta de solo lectura. El modelo ve su nombre, su descripción y su parámetro; decide llamarla y con qué valor, y ese valor se convierte al tipo del parámetro antes de que la consulta se ejecute. stock_result.actions lista cada llamada, escrita completa.
<q:component name="Assistant">
<!-- The model never writes SQL: it picks a tool and its arguments. The
tool is a read-only query you wrote; its q:param says the argument's
type, and the model's value is converted to it before the query runs. -->
<q:agent name="stock" maxIterations="4" timeout="60000" onerror="continue">
<q:instruction>You help a shop owner. Use the tools to look at the data,
then answer in one sentence.</q:instruction>
<q:tool name="low_stock" description="Products with fewer units in stock than `below`">
<q:param name="below" type="integer" default="5" />
<q:function name="lowStock">
<q:query name="rows" datasource="db">
SELECT name, stock FROM products WHERE stock < :below ORDER BY stock
<q:param name="below" value="{below}" type="integer" />
</q:query>
<q:return value="{rows}" />
</q:function>
</q:tool>
<q:execute task="Which products are running out of stock?" />
</q:agent>
<ui:window title="Stock assistant">
<q:if condition="stock_result.success">
<ui:text>{stock}</ui:text>
<q:else>
<ui:alert variant="warning">The assistant did not finish: {stock_result.error.message}</ui:alert>
</q:else>
</q:if>
<!-- Every tool call the agent made, written out. -->
<q:loop items="{stock_result.actions}" var="a">
<ui:text>Called {a.call}</ui:text>
</q:loop>
</ui:window>
</q:component><!-- Structural checks, never the model's exact words: the same tests run
against a real model before every release. -->
<q:test name="the agent looks at the data through its tool" page="/">
<test:visit />
<test:expect text="Called low_stock(" />
<test:expect no-text="did not finish" />
<test:expect table="products" count="4" />
</q:test>En CI, el modelo de reemplazo sigue un guion corto — llamar a la herramienta, y después terminar:
[
{"when": "Which products are running out of stock?",
"replies": ["{\"action\": \"low_stock\", \"args\": {\"below\": 5}}",
"{\"action\": \"finish\", \"result\": \"Monitor (2) and Cable (3) are running out.\"}"]}
]tests/agent.test.q
PASS the agent looks at the data through its tool
1 passed, 0 failedUna herramienta puede hacer todo lo que hace su cuerpo, y un prompt puede empujar al modelo a llamarla: dales a las herramientas solo el acceso que la tarea necesita.
Probado: en CI estas pruebas se ejecutan contra un servidor de modelo de reemplazo, que responde a partir de la primera fuente que recibe; antes de cada versión se ejecutan contra un modelo real (tests/live_ai/test_cookbook_ai.py). Por eso verifican la estructura — qué fuente, qué herramienta, qué muestra la página cuando algo falla — y nunca las palabras del modelo.