personagent 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. personagent-1.0.0/.env.example +423 -0
  2. personagent-1.0.0/LICENSE +21 -0
  3. personagent-1.0.0/MANIFEST.in +7 -0
  4. personagent-1.0.0/PKG-INFO +469 -0
  5. personagent-1.0.0/README.md +425 -0
  6. personagent-1.0.0/data/benchmark/scenarios.holdout.en.jsonl +26 -0
  7. personagent-1.0.0/data/benchmark/scenarios.train.en.jsonl +38 -0
  8. personagent-1.0.0/data/evals/learning.en.jsonl +10 -0
  9. personagent-1.0.0/data/evals/learning.zh.jsonl +10 -0
  10. personagent-1.0.0/data/evals/persona.en.jsonl +18 -0
  11. personagent-1.0.0/data/evals/persona.zh.jsonl +18 -0
  12. personagent-1.0.0/data/evals/speak.en.jsonl +24 -0
  13. personagent-1.0.0/data/evals/speak.zh.jsonl +24 -0
  14. personagent-1.0.0/data/examples.en.jsonl +5 -0
  15. personagent-1.0.0/data/examples.zh.jsonl +5 -0
  16. personagent-1.0.0/data/feedback.en.jsonl +5 -0
  17. personagent-1.0.0/data/feedback.zh.jsonl +5 -0
  18. personagent-1.0.0/data/lorebook.en.json +47 -0
  19. personagent-1.0.0/data/lorebook.zh.json +53 -0
  20. personagent-1.0.0/data/output_filter.en.json +71 -0
  21. personagent-1.0.0/data/output_filter.zh.json +77 -0
  22. personagent-1.0.0/data/persona.example.en.txt +13 -0
  23. personagent-1.0.0/data/persona.example.zh.txt +13 -0
  24. personagent-1.0.0/data/personas/en/bookworm.txt +8 -0
  25. personagent-1.0.0/data/personas/en/dry-wit.txt +9 -0
  26. personagent-1.0.0/data/personas/en/gamer.txt +8 -0
  27. personagent-1.0.0/data/personas/en/warm-listener.txt +10 -0
  28. personagent-1.0.0/data/personas/zh/bookworm.txt +8 -0
  29. personagent-1.0.0/data/personas/zh/dry-wit.txt +9 -0
  30. personagent-1.0.0/data/personas/zh/gamer.txt +8 -0
  31. personagent-1.0.0/data/personas/zh/warm-listener.txt +10 -0
  32. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/LICENSE +21 -0
  33. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/README.md +361 -0
  34. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/README.zh-CN.md +98 -0
  35. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/_conf_schema.json +67 -0
  36. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/logo.png +0 -0
  37. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/main.py +1480 -0
  38. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/metadata.yaml +35 -0
  39. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/personagent_connector.py +204 -0
  40. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/platforms.py +332 -0
  41. personagent-1.0.0/integrations/astrbot/astrbot_plugin_personagent/requirements.txt +1 -0
  42. personagent-1.0.0/persona_agent/__init__.py +46 -0
  43. personagent-1.0.0/persona_agent/__main__.py +3 -0
  44. personagent-1.0.0/persona_agent/access.py +195 -0
  45. personagent-1.0.0/persona_agent/addressing.py +43 -0
  46. personagent-1.0.0/persona_agent/agent.py +729 -0
  47. personagent-1.0.0/persona_agent/candidates.py +526 -0
  48. personagent-1.0.0/persona_agent/channels.py +111 -0
  49. personagent-1.0.0/persona_agent/chat.py +1230 -0
  50. personagent-1.0.0/persona_agent/cli.py +119 -0
  51. personagent-1.0.0/persona_agent/config_env.py +149 -0
  52. personagent-1.0.0/persona_agent/connector.py +468 -0
  53. personagent-1.0.0/persona_agent/dashboard.py +997 -0
  54. personagent-1.0.0/persona_agent/decision.py +130 -0
  55. personagent-1.0.0/persona_agent/decision_log.py +94 -0
  56. personagent-1.0.0/persona_agent/demo.py +812 -0
  57. personagent-1.0.0/persona_agent/dm.py +290 -0
  58. personagent-1.0.0/persona_agent/doctor.py +144 -0
  59. personagent-1.0.0/persona_agent/embeddings.py +167 -0
  60. personagent-1.0.0/persona_agent/endpoints.py +129 -0
  61. personagent-1.0.0/persona_agent/evals.py +1101 -0
  62. personagent-1.0.0/persona_agent/evidence.py +484 -0
  63. personagent-1.0.0/persona_agent/evolution.py +327 -0
  64. personagent-1.0.0/persona_agent/health.py +300 -0
  65. personagent-1.0.0/persona_agent/home.py +69 -0
  66. personagent-1.0.0/persona_agent/ingestion.py +1579 -0
  67. personagent-1.0.0/persona_agent/learning.py +1045 -0
  68. personagent-1.0.0/persona_agent/ledger_admin.py +339 -0
  69. personagent-1.0.0/persona_agent/lineage.py +101 -0
  70. personagent-1.0.0/persona_agent/llm.py +700 -0
  71. personagent-1.0.0/persona_agent/memory.py +717 -0
  72. personagent-1.0.0/persona_agent/messages.py +254 -0
  73. personagent-1.0.0/persona_agent/outbox.py +494 -0
  74. personagent-1.0.0/persona_agent/paths.py +128 -0
  75. personagent-1.0.0/persona_agent/pools.py +130 -0
  76. personagent-1.0.0/persona_agent/preflight.py +565 -0
  77. personagent-1.0.0/persona_agent/proactive.py +207 -0
  78. personagent-1.0.0/persona_agent/promotion.py +602 -0
  79. personagent-1.0.0/persona_agent/prompt.py +582 -0
  80. personagent-1.0.0/persona_agent/prompts.py +1022 -0
  81. personagent-1.0.0/persona_agent/py.typed +0 -0
  82. personagent-1.0.0/persona_agent/ranking.py +117 -0
  83. personagent-1.0.0/persona_agent/reactions.py +754 -0
  84. personagent-1.0.0/persona_agent/retrieval.py +388 -0
  85. personagent-1.0.0/persona_agent/search.py +198 -0
  86. personagent-1.0.0/persona_agent/server.py +1214 -0
  87. personagent-1.0.0/persona_agent/settings.py +509 -0
  88. personagent-1.0.0/persona_agent/setup_wizard.py +1685 -0
  89. personagent-1.0.0/persona_agent/static/app.css +1239 -0
  90. personagent-1.0.0/persona_agent/static/app.js +1812 -0
  91. personagent-1.0.0/persona_agent/static/empty-chats.webp +0 -0
  92. personagent-1.0.0/persona_agent/static/index.html +47 -0
  93. personagent-1.0.0/persona_agent/static/learned-notebook.webp +0 -0
  94. personagent-1.0.0/persona_agent/static/listening.webp +0 -0
  95. personagent-1.0.0/persona_agent/static/mark.svg +19 -0
  96. personagent-1.0.0/persona_agent/stickers.py +692 -0
  97. personagent-1.0.0/persona_agent/storage.py +373 -0
  98. personagent-1.0.0/persona_agent/textproc.py +2904 -0
  99. personagent-1.0.0/persona_agent/thinking.py +77 -0
  100. personagent-1.0.0/persona_agent/transport.py +625 -0
  101. personagent-1.0.0/persona_agent/turns.py +627 -0
  102. personagent-1.0.0/persona_agent/views.py +498 -0
  103. personagent-1.0.0/personagent.egg-info/PKG-INFO +469 -0
  104. personagent-1.0.0/personagent.egg-info/SOURCES.txt +110 -0
  105. personagent-1.0.0/personagent.egg-info/dependency_links.txt +1 -0
  106. personagent-1.0.0/personagent.egg-info/entry_points.txt +2 -0
  107. personagent-1.0.0/personagent.egg-info/requires.txt +15 -0
  108. personagent-1.0.0/personagent.egg-info/top_level.txt +1 -0
  109. personagent-1.0.0/pyproject.toml +63 -0
  110. personagent-1.0.0/requirements.txt +13 -0
  111. personagent-1.0.0/setup.cfg +4 -0
  112. personagent-1.0.0/setup.py +43 -0
@@ -0,0 +1,423 @@
1
+ # personagent settings. `personagent init` fills in the first block for you.
2
+ # Each setting is described on the lines above it; a blank value means the
3
+ # default the description gives.
4
+
5
+ # ============================================================================
6
+ # REQUIRED
7
+ # ============================================================================
8
+
9
+ # The chat model: any OpenAI-compatible service, hosted or local (Ollama).
10
+ # The key is all that `personagent chat` needs.
11
+ LLM_API_KEY=
12
+ # The API address as the provider documents it: a root such as
13
+ # https://api.deepseek.com, a version base such as .../v1 or .../api/paas/v4,
14
+ # or the full /chat/completions URL.
15
+ LLM_BASE_URL=https://api.deepseek.com
16
+ LLM_MODEL=deepseek-flash
17
+
18
+ # The bot's name. It notices being addressed when a message contains it.
19
+ PERSONA_NAME=
20
+
21
+ # Language of the agent: en or zh. Picks the bundled examples, feedback,
22
+ # lorebook and output filters (data/*.en.* or data/*.zh.*), the reply checks
23
+ # and the words its commands understand. It does not translate your persona.
24
+ AGENT_LANG=en
25
+
26
+ # ============================================================================
27
+ # PERSONA AND ACCESS
28
+ # ============================================================================
29
+
30
+ # The persona document, and its optional companion card, both relative to
31
+ # AGENT_HOME. Until persona.txt exists, the bundled
32
+ # data/persona.example.<lang>.txt stands in.
33
+ # The card holds per-persona reply-style opt-ins, such as
34
+ # {"reply_style": {"emoji": true, "charsets": ["music"], "max_chars": 320}};
35
+ # charsets are ellipsis, music and arrows. Without a card no emoji and no
36
+ # optional characters are allowed. A `[style]` block at the end of the persona
37
+ # document sets the six register knobs: length, vent, recs, good_news,
38
+ # particles and fatigue.
39
+ PERSONA_FILE=persona.txt
40
+ PERSONA_CARD_FILE=persona.card.json
41
+
42
+ # The persona's time zone as a UTC offset in hours, from -12 to 14, such as
43
+ # 8 or -5. It sets the time shown to the model and the 02:00-07:00 sleep
44
+ # window, when the bot does not start conversations. Blank = UTC+8 when
45
+ # AGENT_LANG is zh, this machine's offset otherwise.
46
+ PERSONA_TZ_OFFSET_HOURS=
47
+
48
+ # A label recorded on every evidence event beside a hash of the persona text.
49
+ # Editing the persona keeps what the bot learned: every version under one
50
+ # label counts as the same character (runtime/persona_lineage.json). Change
51
+ # the label to start a new character with a clean slate.
52
+ PERSONA_VERSION=
53
+
54
+ # Ids are written "<platform>:<id>", the platform spelled as the connector
55
+ # names it (AstrBot's adapter names: telegram, discord, slack, kook, lark, ...),
56
+ # such as telegram:-1001234567 or discord:4242. A bare id, or qq:<id>, is a
57
+ # QQ number. None of these is needed for `personagent chat`.
58
+
59
+ # The admin: the one person who runs the bot, with an account on as many
60
+ # platforms as you list. The bot is closest to them, they may manage what it
61
+ # remembers about a group, and they may teach it on their own.
62
+ ADMIN_IDS=
63
+ ADMIN_NAME=
64
+ # How the bot relates to the admin, such as brother, boss or friend.
65
+ # Blank = not mentioned in prompts.
66
+ ADMIN_RELATIONSHIP=
67
+
68
+ # The groups the bot answers, and who besides the admin may DM it. Each
69
+ # platform is gated on its own. A platform with no entries here is not
70
+ # restricted by the agent: every QQ group is answered, a QQ DM needs the admin
71
+ # or an entry, and any other platform is left to the connector's allowlist
72
+ # (such as the AstrBot plugin's groups and dm_users). One entry for a platform
73
+ # restricts that platform to its entries. DM users get the "ordinary friend"
74
+ # persona: looser than in a group, but not the admin's.
75
+ ACCESS_GROUPS=
76
+ ACCESS_DM_USERS=
77
+
78
+ # ============================================================================
79
+ # CONNECTORS (the AstrBot plugin, Satori, Matrix)
80
+ # ============================================================================
81
+
82
+ # POST /v1/events takes messages forwarded from chat platforms and returns the
83
+ # replies in its response (docs/connectors.md).
84
+ # CONNECTOR_TOKEN is the secret the connector signs each request with: a bearer
85
+ # header plus HMAC-SHA256(timestamp + "." + nonce + "." + exact JSON body),
86
+ # checked against replays. Blank is accepted only on loopback; forwarding from
87
+ # another host needs HTTPS (or a private tunnel ending on loopback) and a token.
88
+ # `personagent connect astrbot` writes the same token on both sides.
89
+ CONNECTOR_TOKEN=
90
+ # Events whose sent_at is older than this many seconds are refused even inside
91
+ # a fresh request, which allows an adapter's delayed delivery for up to 24 h.
92
+ CONNECTOR_MAX_EVENT_AGE_S=86400
93
+ # Connector platforms whose ids arrive bare instead of "<platform>:<id>".
94
+ # Set it to the connector's QQ adapter (AstrBot calls it aiocqhttp) to run QQ
95
+ # through a connector: QQ ids then stay plain numbers, the way NapCat and every
96
+ # store on disk spell them. Leave it blank otherwise.
97
+ # Naming a platform here lets its connector address QQ conversations, because
98
+ # bare ids are what the QQ entries of ADMIN_IDS, ACCESS_GROUPS and
99
+ # ACCESS_DM_USERS are compared against. Only QQ's adapter belongs here.
100
+ CONNECTOR_QQ_PLATFORMS=
101
+ # Messages nobody asked for (openers, the follow-up question after a rejection,
102
+ # the excuse when the model call fails) reach a forwarded platform only through
103
+ # a connector that polls POST /v1/outbox and says so ("capabilities":
104
+ # ["outbox"], see docs/connectors.md). false turns the outbox off: the endpoint
105
+ # answers 404, and such messages go out on QQ through NapCat only.
106
+ CONNECTOR_OUTBOX_ENABLED=true
107
+ # Connector turns run at the same time. /v1/events holds a slot for a whole
108
+ # turn, so it has its own budget. Blank = QQ_ONEBOT_MAX_INFLIGHT.
109
+ CONNECTOR_MAX_INFLIGHT=
110
+
111
+ # ============================================================================
112
+ # SERVICE AND STORAGE
113
+ # ============================================================================
114
+
115
+ # Where personagent listens. Loopback by default (blank too), so nothing is
116
+ # exposed. A split deployment may set SERVER_HOST=0.0.0.0, but then startup
117
+ # requires CONNECTOR_TOKEN, and /v1/onebot is off unless QQ_ONEBOT_SECRET is set.
118
+ SERVER_HOST=127.0.0.1
119
+ SERVER_PORT=8080
120
+ # The web page at http://127.0.0.1:<SERVER_PORT>/: status, why it spoke or
121
+ # stayed quiet, and what it learned, with promote / reject / roll back. It
122
+ # opens only through its private link (the token in runtime/dashboard.token),
123
+ # which `personagent run` and `personagent doctor` print. false leaves it out.
124
+ DASHBOARD_ENABLED=true
125
+ # The largest request body either endpoint accepts, in bytes.
126
+ SERVER_MAX_BODY_BYTES=8000000
127
+ # Logs go to the console. A path here also writes rotating log files, which can
128
+ # contain conversation details; keep it outside the repository or gitignored.
129
+ SERVER_LOG_FILE=
130
+
131
+ # The deployment folder: .env, persona.txt, runtime/ and the instance lock are
132
+ # resolved under it. Blank = this checkout, or the folder `personagent init`
133
+ # set up (~/personagent by default). AGENT_RUNTIME_DIR must stay beneath it.
134
+ # Set it to run several personas from one installed copy.
135
+ AGENT_HOME=
136
+
137
+ # Everything the bot learns or remembers (memory, evaluations, ledgers,
138
+ # learned pools, sticker notes) is written here, never into data/. Relative
139
+ # paths are under AGENT_HOME. runtime/ is gitignored; another folder inside a
140
+ # repository needs the same, because it holds real conversations.
141
+ AGENT_RUNTIME_DIR=runtime
142
+
143
+ # false keeps the service up but the agent answers nothing.
144
+ AGENT_ENABLED=true
145
+
146
+ # ============================================================================
147
+ # OPTIONAL FEATURES (safe to leave as they are)
148
+ # ============================================================================
149
+
150
+ # --- Model calls ---
151
+ # Per-request timeout in seconds, and retries after a transient error.
152
+ LLM_TIMEOUT_S=120
153
+ LLM_MAX_RETRIES=2
154
+ # Another model name for DMs, served by the same endpoint and key as LLM_MODEL.
155
+ # The exception: a name equal to LLM_FALLBACK_MODEL is served where the
156
+ # fallback is. Blank = LLM_MODEL.
157
+ LLM_DM_MODEL=
158
+ # The cheap first step when the bot speaks on its own (judging whether to join
159
+ # in, follow-ups, openers) asks this model only "reply or stay quiet?". The
160
+ # line that is sent is written by the main model. Blank = LLM_FALLBACK_MODEL,
161
+ # else the main model. Point it at the cheapest model you have.
162
+ LLM_JUDGE_MODEL=
163
+
164
+ # --- Fallback when busy (a cheaper model while the main one is loaded) ---
165
+ LLM_FALLBACK_MODEL=
166
+ # The fallback model's own endpoint, so an outage at the main provider is not
167
+ # also the fallback's. Blank = LLM_BASE_URL and LLM_API_KEY. Only a
168
+ # LLM_FALLBACK_MODEL different from LLM_MODEL is sent here, together with the
169
+ # judge, self-eval and reaction calls whose models default to it. A blank key
170
+ # with a URL on another provider sends LLM_API_KEY there, so give it its own.
171
+ # Accepts the same spellings as LLM_BASE_URL.
172
+ LLM_FALLBACK_BASE_URL=
173
+ LLM_FALLBACK_API_KEY=
174
+ # DeepSeek's `thinking` field turns hidden reasoning off for the judge, the
175
+ # search decision and the sticker tagger. DeepSeek, Zhipu, Moonshot and
176
+ # Volcengine hosts get it; OpenAI, Groq, Gemini, OpenRouter, Mistral and
177
+ # Anthropic do not; any other endpoint is asked on its first such call, and
178
+ # the answer is kept per model. true = send it to the fallback's host without
179
+ # asking. An endpoint that answers 400 to it is not sent it again.
180
+ LLM_FALLBACK_THINKING=false
181
+ # When LLM_RATE_THRESHOLD calls fall within LLM_RATE_WINDOW_S seconds, the
182
+ # next LLM_FALLBACK_DURATION_S seconds use the fallback model.
183
+ LLM_RATE_WINDOW_S=120
184
+ LLM_RATE_THRESHOLD=30
185
+ LLM_FALLBACK_DURATION_S=180
186
+ # Seconds a model that answered 429 (rate limited) is skipped. Shorter than
187
+ # LLM_FALLBACK_DURATION_S because a throttled model is metered, not broken.
188
+ # Any other failure (a 400, retries used up) keeps the model out for
189
+ # LLM_FALLBACK_DURATION_S.
190
+ LLM_RATE_LIMIT_COOLDOWN_S=20
191
+
192
+ # --- Conversation ---
193
+ # Messages from others before the bot considers joining in unasked.
194
+ CHAT_TRIGGER_COUNT=30
195
+ # Messages of history kept per conversation for the prompt.
196
+ CHAT_CONTEXT_MESSAGES=120
197
+ # Seconds after the bot's reply during which new messages count as follow-ups.
198
+ CHAT_FOLLOWUP_WINDOW_S=120
199
+ # The notes file under AGENT_RUNTIME_DIR, and how many notes each
200
+ # conversation keeps.
201
+ MEMORY_FILE=memory.json
202
+ MEMORY_MAX_PER_CONVERSATION=50
203
+
204
+ # --- Web search (looks up unfamiliar things) ---
205
+ # With a Tavily key it uses Tavily (more reliable); blank uses DuckDuckGo,
206
+ # which needs no key (the `ddgs` package from requirements.txt).
207
+ TAVILY_API_KEY=
208
+
209
+ # --- Vision (images and stickers) ---
210
+ # Blank = no vision model, only OCR, which costs nothing. Any OpenAI-compatible
211
+ # vision model works; give it its provider's key and base URL here, since there
212
+ # is no default endpoint.
213
+ VISION_MODEL=
214
+ VISION_API_KEY=
215
+ VISION_BASE_URL=
216
+ # The largest decoded image accepted from base64, a file or HTTP, in bytes.
217
+ VISION_MAX_IMAGE_BYTES=5000000
218
+
219
+ # --- Self-eval (scores each reply 1-5 into eval.jsonl, in the background) ---
220
+ # Off by default, so a first run costs least. Prefer a model from another
221
+ # vendor than the chat model: a model family is blind to its own failure
222
+ # modes. An EVAL_MODEL containing "moonshot" or "kimi" goes to the vision
223
+ # endpoint above.
224
+ EVAL_ENABLED=false
225
+ EVAL_MODEL=
226
+ EVAL_FILE=eval.jsonl
227
+
228
+ # --- Proactive messages (the bot sometimes speaks first) ---
229
+ # Off by default. When on, a background loop may start a conversation after a
230
+ # chat has been quiet for a while (outside the sleep window), with cooldowns
231
+ # and a low chance, and the model is told to stay quiet unless it has
232
+ # something to say. Groups and DMs, but only people and groups the bot has
233
+ # seen talk since it started. On QQ it sends through NapCat, and DMs go only to
234
+ # the QQ ids in ADMIN_IDS and ACCESS_DM_USERS. Elsewhere it needs a connector
235
+ # that pulls the outbox (CONNECTOR_OUTBOX_ENABLED), and DMs go to anyone that
236
+ # connector lets through.
237
+ PROACTIVE_ENABLED=false
238
+ # Platforms it may speak first on, comma-separated (qq, telegram, discord,
239
+ # ...); blank = every platform it can reach.
240
+ PROACTIVE_PLATFORMS=
241
+ # How often the loop checks, in seconds (25 min).
242
+ PROACTIVE_INTERVAL_S=1500
243
+ # A group must be quiet this long first, in seconds (45 min).
244
+ PROACTIVE_MIN_SILENCE_S=2700
245
+ # Least time between openers in one group, in seconds (3 h).
246
+ PROACTIVE_COOLDOWN_S=10800
247
+ # Chance per eligible check, 0 to 1.
248
+ PROACTIVE_PROB=0.25
249
+ # A DM must be quiet this long first, in seconds (4 h).
250
+ PROACTIVE_DM_MIN_SILENCE_S=14400
251
+ # Least time between DMs to one person, in seconds (24 h).
252
+ PROACTIVE_DM_COOLDOWN_S=86400
253
+ # Chance per eligible check for a DM, 0 to 1.
254
+ PROACTIVE_DM_PROB=0.2
255
+
256
+ # --- Learning from reactions ---
257
+ # On by default. Each reply waits a while for a reaction aimed at it: a quote
258
+ # of the bot's message, a mention or its name, or in a DM the other person's
259
+ # next message. One cheap model call reads the reaction as a correction,
260
+ # rejection, positive or neutral, and filters out banter and trolling.
261
+ # A reaction is recorded as evidence (append-only, in
262
+ # runtime/evidence.<lang>.jsonl) and may propose a candidate. A candidate
263
+ # changes nothing until it is promoted (next block). Every reading is also
264
+ # logged in candidates.jsonl (src=user_reaction).
265
+ REACT_LEARN_ENABLED=true
266
+ # How long a reply waits for a reaction, in seconds (15 min).
267
+ REACT_TTL_S=900
268
+ # Replies tracked per conversation.
269
+ REACT_MAX_PENDING=4
270
+ # The model that reads reactions. Blank = LLM_JUDGE_MODEL (the cheapest
271
+ # configured).
272
+ REACT_MODEL=
273
+ # After a rejection, how long the bot's next reply still counts as its second
274
+ # try, in seconds.
275
+ REACT_FIX_WINDOW_S=600
276
+ # After an accepted rejection, the bot may later ask what the person meant
277
+ # (delayed, with a cooldown).
278
+ REACT_ELICIT_ENABLED=true
279
+ # Seconds to wait before asking, so it never talks over its own reply to the
280
+ # rejection.
281
+ REACT_ELICIT_DELAY_S=120
282
+ # Least time between such questions in one conversation, in seconds.
283
+ REACT_ELICIT_COOLDOWN_S=3600
284
+
285
+ # --- Promotion (when learned material may change replies) ---
286
+ # A reaction is evidence, and reading it creates a candidate. Promotion gives a
287
+ # candidate a say in future replies; a rollback or a newer candidate takes it
288
+ # away. Only promoted candidates are used, through
289
+ # runtime/promoted.{examples,feedback}.<lang>.jsonl, a view rebuilt from the
290
+ # append-only ledger and never edited by hand.
291
+ # The defaults make one thing impossible: a single automatic signal changing
292
+ # for good how the bot talks. Loosen them only while watching the results.
293
+ # Review what is waiting with `personagent learned list`.
294
+ #
295
+ # Strong = an explicit correction with a concrete better line, from the person
296
+ # the reply was meant for, or a second try that person accepted.
297
+ # Weaker = a rejection with nothing concrete, a correction from a bystander,
298
+ # or the bot's own review. Real evidence, not a mandate.
299
+ # Weak = laughter, agreement, banter, the bot's own score. Never promotes
300
+ # anything on its own, so good examples are promoted by you.
301
+ # Being the admin does not stand in for being the person the reply was for,
302
+ # and conflicting evidence blocks automatic promotion.
303
+ #
304
+ # false = nothing is promoted without you.
305
+ PROMOTE_AUTO_ENABLED=true
306
+ # Distinct agreeing events needed (at least 2).
307
+ PROMOTE_MIN_EVENTS=2
308
+ # Of which this many must be strong (at least 1).
309
+ PROMOTE_MIN_STRONG=1
310
+ # From this many different people (at least 1). One person can supply both
311
+ # events, by correcting a reply and then accepting the retry; an honest
312
+ # clarification looks exactly like misuse of it. 2 stops a stranger from
313
+ # teaching the bot alone in a room: the admin is exempt, and a candidate held
314
+ # back this way still waits for you to approve.
315
+ PROMOTE_MIN_SPEAKERS=1
316
+ # Evidence older than this many days stops counting; 0 = no limit.
317
+ PROMOTE_EVIDENCE_MAX_AGE_DAYS=30
318
+ # Never combine evidence from different conversations.
319
+ PROMOTE_REQUIRE_SAME_CONVERSATION=true
320
+
321
+ # --- Size of the learned pools ---
322
+ # Each reply picks 4 examples and 6 better-reply pairs from the read-only data/
323
+ # seeds, the learned runtime/ pools and the promoted views. These caps bound
324
+ # the machine-written part: they size the promoted views, and the offline
325
+ # tools apply them to what they write. Older material came from an older
326
+ # prompt, and without a cap it outvotes the recent. Raise them for very varied
327
+ # topics; 0 = no cap. What you wrote or approved yourself is never counted or
328
+ # dropped.
329
+ PROMOTE_MAX_EXAMPLES=500
330
+ PROMOTE_MAX_FEEDBACK=500
331
+
332
+ # --- Self-improvement loop (from self-eval scores to candidates) ---
333
+ # Off by default. When on, a background loop takes new low scores from
334
+ # eval.jsonl, has a model diagnose each and draft a better reply, and files it
335
+ # as a candidate. Nothing is applied: a self-diagnosis is one signal nobody
336
+ # witnessed, so it waits for a real reaction to agree, or for you to promote it.
337
+ # The same with a review step: python tools/auto_reviewer.py --apply
338
+ # Needs EVAL_ENABLED=true.
339
+ EVOLVE_AUTO_ENABLED=false
340
+ # Hours between looks at new scores.
341
+ EVOLVE_INTERVAL_HOURS=6
342
+ # A score at or below this counts as a miss (3 = drifting into an assistant's
343
+ # voice, the band this loop is for).
344
+ EVOLVE_THRESHOLD=3
345
+ # Most entries diagnosed per round.
346
+ EVOLVE_BATCH=5
347
+ # Blank = EVAL_MODEL, else the chat model.
348
+ EVOLVE_MODEL=
349
+
350
+ # --- Retrieval by meaning (embeddings) ---
351
+ # Retrieval ranks examples, pairs and notes on the words they share with the
352
+ # message (BM25), where they were learned (this conversation, then this
353
+ # persona, then the shipped seeds) and how recent they are. An embedding model
354
+ # adds meaning: a row that says the same thing in other words can win.
355
+ # Blank EMBEDDING_MODEL = off. Any OpenAI-compatible /embeddings endpoint
356
+ # works (OpenAI, SiliconFlow, Zhipu, a local Ollama). Vectors are cached in
357
+ # AGENT_RUNTIME_DIR/embeddings.jsonl. A reply waits at most 3 seconds for its
358
+ # message to be embedded; a slow or failing endpoint is logged once, and
359
+ # retrieval ranks without it until it answers.
360
+ EMBEDDING_MODEL=
361
+ # Blank = LLM_BASE_URL with LLM_API_KEY. A URL of its own gets only
362
+ # EMBEDDING_API_KEY (blank for an endpoint that needs no key). Accepts a root,
363
+ # a version base (/v1, /api/paas/v4) or the full /embeddings URL.
364
+ EMBEDDING_BASE_URL=
365
+ EMBEDDING_API_KEY=
366
+
367
+ # --- Offline tools (not read by the running bot) ---
368
+ # Listed so the startup check knows these names are real settings.
369
+ # tools/prompt_lab.py writes through a different vendor than the agent under
370
+ # test, which makes it a measurement rather than a mirror. Needs
371
+ # `pip install -e ".[judge]"`.
372
+ ANTHROPIC_API_KEY=
373
+ ANTHROPIC_BASE_URL=
374
+ # Required by the lab: a model the vendor above serves.
375
+ LAB_MODEL=
376
+ # tools/auto_reviewer.py; blank = EVAL_MODEL, else the chat model.
377
+ REVIEWER_MODEL=
378
+ # The scoring model of `personagent eval` (persona and learning suites) and
379
+ # tools/evolution_benchmark.py --judge, again from another vendor than the one
380
+ # measured. BENCH_JUDGE_BASE_URL and BENCH_JUDGE_API_KEY default to
381
+ # LLM_BASE_URL and LLM_API_KEY.
382
+ BENCH_JUDGE_MODEL=
383
+ BENCH_JUDGE_API_KEY=
384
+ BENCH_JUDGE_BASE_URL=
385
+ BENCH_EVAL_DELAY_S=
386
+
387
+ # --- Ledger size warnings ---
388
+ # The evidence log and the candidate ledger are append-only and never shrink.
389
+ # These are the sizes, in bytes, at which each starts warning in the log; they
390
+ # do not cap or rotate anything.
391
+ LEDGER_EVIDENCE_WARN_BYTES=
392
+ LEDGER_CANDIDATES_WARN_BYTES=
393
+
394
+ # ============================================================================
395
+ # QQ THROUGH NAPCAT (OneBot v11)
396
+ # ============================================================================
397
+
398
+ # QQ comes in through AstrBot (CONNECTOR_QQ_PLATFORMS above). The direct
399
+ # OneBot route, NapCat posting to /v1/onebot, is deprecated and still served.
400
+ # Which QQ groups the bot answers is ACCESS_GROUPS.
401
+
402
+ # The bot account's QQ number, on either route.
403
+ QQ_BOT_ID=
404
+
405
+ # NapCat's HTTP server, such as http://127.0.0.1:3000. On the direct route
406
+ # every reply goes out through it; through AstrBot it is only used for
407
+ # proactive sends and the sweep for mentions missed while the bot was busy.
408
+ # Blank = not used. Events never come this way; they reach SERVER_PORT.
409
+ QQ_ONEBOT_URL=
410
+
411
+ # If NapCat hands over received images as file:// paths (its local-cache
412
+ # mode), set this to that cache folder. Blank refuses every file:// path.
413
+ QQ_ONEBOT_IMAGE_DIR=
414
+
415
+ # The `secret` in NapCat's HTTP client config. Read only by the direct route:
416
+ # with it set, each /v1/onebot body must carry a matching
417
+ # `x-signature: sha1=<hex>` header. Blank accepts only programs on this
418
+ # machine, and nothing at all when SERVER_HOST is not loopback.
419
+ QQ_ONEBOT_SECRET=
420
+
421
+ # Expensive OneBot requests handled at the same time. Requests beyond this get
422
+ # HTTP 429 instead of piling up memory and model calls.
423
+ QQ_ONEBOT_MAX_INFLIGHT=64
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Qiankang (Kant) Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ # What setup.py bundles into the wheel, so a wheel built from the sdist has it too.
2
+ include requirements.txt .env.example
3
+ graft data
4
+ graft integrations/astrbot/astrbot_plugin_personagent
5
+ # The sdist builds the package; the test suite runs from a checkout.
6
+ prune tests
7
+ global-exclude *.py[cod]