跳到正文

用 JSON 回答给消息分类 ​

机器翻译

本页由英文原文机器翻译而来,尚未经过母语审校,欢迎在 GitHub 上提出修改。内容如有出入,以英文原文为准。

任务: 把每条支持消息归入一个由模型决定的类别,保存在数据表中。

yaml
# Recipe: the model reads free text and answers with fields you store.
paths:
  components: ./components
  migrations: ./migrations

datasources:
  db:
    driver: sqlite
    database: ./data/helpdesk.db

llm:
  model: phi3
sql
CREATE TABLE tickets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    message TEXT NOT NULL,
    category TEXT NOT NULL CHECK (category IN ('billing', 'shipping', 'other')),
    urgent INTEGER NOT NULL DEFAULT 0
);

responseFormat="json" 要求模型返回 JSON 并解析它:ticket 是一个对象。 它的字段和其他输入一样——页面在保存之前检查类别,而动作自身的 q:param 规则在调用模型之前就会运行。

xml
<q:component name="Helpdesk">
  <q:action name="open" method="POST">
    <q:param name="message" required="true" minlength="10" />

    <!-- responseFormat="json": the model is asked for JSON, and the value is
         the parsed object — ticket.category, ticket.urgent. -->
    <q:llm name="ticket" responseFormat="json" temperature="0">
      <q:prompt>Classify this customer message. Answer with JSON only:
{"category": "billing" or "shipping" or "other", "urgent": true or false}
Message: {message}</q:prompt>
    </q:llm>

    <!-- A model's answer is input like any other: a category it made up is
         filed as "other" (the table's CHECK would refuse it). -->
    <q:set name="category" value="{ticket.category if ticket.category in ['billing', 'shipping'] else 'other'}" />

    <q:query name="saved" datasource="db">
      INSERT INTO tickets (message, category, urgent) VALUES (:message, :category, :urgent)
      <q:param name="message" value="{message}" type="string" />
      <q:param name="category" value="{category}" type="string" />
      <q:param name="urgent" value="{ticket.urgent == true}" type="boolean" />
    </q:query>
    <q:redirect url="/" flash="Filed under {category}." />
  </q:action>

  <q:query name="tickets" datasource="db">
    SELECT category, urgent, message FROM tickets ORDER BY id DESC
  </q:query>

  <ui:window title="Helpdesk">
    <q:if condition="flash">
      <ui:alert variant="success">{flash}</ui:alert>
    </q:if>
    <ui:form on-submit="open">
      <ui:input bind="message" rows="3" placeholder="How can we help?" />
      <ui:button variant="primary">Send</ui:button>
    </ui:form>
    <ui:table source="{tickets}" />
  </ui:window>
</q:component>
xml
<!-- Structural checks, never the model's exact words: the same tests run
     against a real model before every release. -->
<q:test name="a message is filed with the model's fields" page="/">
  <test:submit action="open" message="I was charged twice for order 1042, please refund one." />
  <test:expect redirect="/" />
  <test:expect table="tickets" count="1" where="category = 'billing'" />
</q:test>

<q:test name="a message too short never reaches the model" page="/">
  <test:submit action="open" message="help" />
  <test:expect error="message" />
  <test:expect table="tickets" count="0" />
</q:test>

<q:test name="a category the model made up is filed as other" page="/">
  <test:submit action="open" message="Please write me a poem about the sea." />
  <test:expect redirect="/" flash="Filed under other." />
  <test:expect table="tickets" count="1" where="category = 'other'" />
</q:test>
json
[
  {"when": "charged twice", "replies": ["{\"category\": \"billing\", \"urgent\": true}"]},
  {"when": "a poem about the sea", "replies": ["{\"category\": \"poetry\", \"urgent\": false}"]}
]
text
tests/tickets.test.q
  PASS  a message is filed with the model's fields
  PASS  a message too short never reaches the model
  PASS  a category the model made up is filed as other
3 passed, 0 failed

已测试: 在 CI 中,这些测试针对一个替身模型服务器运行,它根据收到的第一个来源作答;每次发布前,它们针对真实模型运行(tests/live_ai/test_cookbook_ai.py)。因此它们检查的是结构——哪个来源、哪个工具、失败时页面显示什么——而从不检查模型的措辞。

参见 IA-1。

MIT 许可证 · 使用 VitePress 构建